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-02 05:45:25.932
2025-07-02 05:45:25.937 act = <firebird.qa.plugin.Action object at [hex]>
2025-07-02 05:45:25.943
2025-07-02 05:45:25.949 @pytest.mark.version('>=3')
2025-07-02 05:45:25.955 def test_1(act: Action):
2025-07-02 05:45:25.964 act.expected_stdout = expected_stdout
2025-07-02 05:45:25.975 act.execute()
2025-07-02 05:45:25.985 >       assert act.clean_stdout == act.clean_expected_stdout
2025-07-02 05:45:25.994
2025-07-02 05:45:26.006 tests/bugs/core_2969_test.py:1211:
2025-07-02 05:45:26.015 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:26.023
2025-07-02 05:45:26.030 ops = ('==',), results = (False,)
2025-07-02 05:45:26.037 expls = ('%(py2)s\n{%(py2)s = %(py0)s.clean_stdout\n} == %(py6)s\n{%(py6)s = %(py4)s.clean_expected_stdout\n}',)
2025-07-02 05:45:26.045 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-02 05:45:26.051
2025-07-02 05:45:26.058 def _call_reprcompare(
2025-07-02 05:45:26.064 ops: Sequence[str],
2025-07-02 05:45:26.070 results: Sequence[bool],
2025-07-02 05:45:26.077 expls: Sequence[str],
2025-07-02 05:45:26.082 each_obj: Sequence[object],
2025-07-02 05:45:26.088 ) -> str:
2025-07-02 05:45:26.094 for i, res, expl in zip(range(len(ops)), results, expls):
2025-07-02 05:45:26.100 try:
2025-07-02 05:45:26.106 done = not res
2025-07-02 05:45:26.111 except Exception:
2025-07-02 05:45:26.117 done = True
2025-07-02 05:45:26.123 if done:
2025-07-02 05:45:26.129 break
2025-07-02 05:45:26.135 if util._reprcompare is not None:
2025-07-02 05:45:26.147 >           custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1])
2025-07-02 05:45:26.157
2025-07-02 05:45:26.167 ../lib/python3.11/site-packages/_pytest/assertion/rewrite.py:499:
2025-07-02 05:45:26.181 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:26.192
2025-07-02 05:45:26.201 op = '=='
2025-07-02 05:45:26.210 left = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1var_...    yyy\n1var_997                        yyy\n1var_998                        yyy\n1var_999                        yyy'
2025-07-02 05:45:26.219 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-02 05:45:26.225
2025-07-02 05:45:26.232 def callbinrepr(op, left: object, right: object) -> Optional[str]:
2025-07-02 05:45:26.238 """Call the pytest_assertrepr_compare hook and prepare the result.
2025-07-02 05:45:26.248
2025-07-02 05:45:26.256 This uses the first result from the hook and then ensures the
2025-07-02 05:45:26.262 following:
2025-07-02 05:45:26.271 * Overly verbose explanations are truncated unless configured otherwise
2025-07-02 05:45:26.282 (eg. if running in verbose mode).
2025-07-02 05:45:26.291 * Embedded newlines are escaped to help util.format_explanation()
2025-07-02 05:45:26.301 later.
2025-07-02 05:45:26.314 * If the rewrite mode is used embedded %-characters are replaced
2025-07-02 05:45:26.327 to protect later % formatting.
2025-07-02 05:45:26.340
2025-07-02 05:45:26.349 The result can be formatted by util.format_explanation() for
2025-07-02 05:45:26.362 pretty printing.
2025-07-02 05:45:26.372 """
2025-07-02 05:45:26.380 >       hook_result = ihook.pytest_assertrepr_compare(
2025-07-02 05:45:26.386 config=item.config, op=op, left=left, right=right
2025-07-02 05:45:26.394 )
2025-07-02 05:45:26.399
2025-07-02 05:45:26.404 ../lib/python3.11/site-packages/_pytest/assertion/__init__.py:141:
2025-07-02 05:45:26.410 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:26.424
2025-07-02 05:45:26.435 self = <HookCaller 'pytest_assertrepr_compare'>
2025-07-02 05:45:26.448 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-02 05:45:26.461 firstresult = False
2025-07-02 05:45:26.469
2025-07-02 05:45:26.481 def __call__(self, **kwargs: object) -> Any:
2025-07-02 05:45:26.490 """Call the hook.
2025-07-02 05:45:26.497
2025-07-02 05:45:26.507 Only accepts keyword arguments, which should match the hook
2025-07-02 05:45:26.518 specification.
2025-07-02 05:45:26.528
2025-07-02 05:45:26.537 Returns the result(s) of calling all registered plugins, see
2025-07-02 05:45:26.544 :ref:`calling`.
2025-07-02 05:45:26.556 """
2025-07-02 05:45:26.567 assert (
2025-07-02 05:45:26.576 not self.is_historic()
2025-07-02 05:45:26.583 ), "Cannot directly call a historic hook - use call_historic instead."
2025-07-02 05:45:26.589 self._verify_all_args_are_provided(kwargs)
2025-07-02 05:45:26.595 firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
2025-07-02 05:45:26.600 >       return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
2025-07-02 05:45:26.605
2025-07-02 05:45:26.610 ../lib/python3.11/site-packages/pluggy/_hooks.py:493:
2025-07-02 05:45:26.615 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:26.620
2025-07-02 05:45:26.625 self = <_pytest.config.PytestPluginManager object at [hex]>
2025-07-02 05:45:26.631 hook_name = 'pytest_assertrepr_compare'
2025-07-02 05:45:26.639 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-02 05:45:26.646 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-02 05:45:26.652 firstresult = False
2025-07-02 05:45:26.658
2025-07-02 05:45:26.664 def _hookexec(
2025-07-02 05:45:26.671 self,
2025-07-02 05:45:26.679 hook_name: str,
2025-07-02 05:45:26.686 methods: Sequence[HookImpl],
2025-07-02 05:45:26.692 kwargs: Mapping[str, object],
2025-07-02 05:45:26.699 firstresult: bool,
2025-07-02 05:45:26.705 ) -> object | list[object]:
2025-07-02 05:45:26.712 # called from all hookcaller instances.
2025-07-02 05:45:26.719 # enable_tracing will set its own wrapping function at self._inner_hookexec
2025-07-02 05:45:26.726 >       return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
2025-07-02 05:45:26.732
2025-07-02 05:45:26.738 ../lib/python3.11/site-packages/pluggy/_manager.py:115:
2025-07-02 05:45:26.744 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:26.750
2025-07-02 05:45:26.761 config = <_pytest.config.Config object at [hex]>, op = '=='
2025-07-02 05:45:26.771 left = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1var_...    yyy\n1var_997                        yyy\n1var_998                        yyy\n1var_999                        yyy'
2025-07-02 05:45:26.779 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-02 05:45:26.786
2025-07-02 05:45:26.792 def pytest_assertrepr_compare(config: Config, op: str, left: object, right: object) -> Optional[List[str]]:
2025-07-02 05:45:26.798 """Returns explanation for comparisons in failing assert expressions.
2025-07-02 05:45:26.804
2025-07-02 05:45:26.810 If both objects are `str`, uses `difflib.ndiff` to provide explanation.
2025-07-02 05:45:26.816 """
2025-07-02 05:45:26.822 if isinstance(left, str) and isinstance(right, str) and op == "==":
2025-07-02 05:45:26.830 # 16.11.2023, pzotov: we have to put empty string at the beginning of each comparing lists.
2025-07-02 05:45:26.836 # Otherwise first diff will be at the same line as 'assert' phrase, which causes readability be poor.
2025-07-02 05:45:26.845 #
2025-07-02 05:45:26.859 left_lines = ['']
2025-07-02 05:45:26.871 left_lines.extend(left.splitlines())
2025-07-02 05:45:26.880 right_lines = ['']
2025-07-02 05:45:26.889 right_lines.extend(right.splitlines())
2025-07-02 05:45:26.896
2025-07-02 05:45:26.903 # 16.11.2023, pzotov
2025-07-02 05:45:26.909 # ndiff output must be interpreted as following:
2025-07-02 05:45:26.915 #     * "E     - <some text>" ==> MISSED line (it was in EXPECTED text but absent in actual one).
2025-07-02 05:45:26.922 #     * "E     + <some_text>" ==> EXCESSIVE line (it is not in EXPECTED text but did appear in actual).
2025-07-02 05:45:26.928 # But for QA-purposes, this output must answer the question:
2025-07-02 05:45:26.935 #     "what must be changed in ACTUAL output so that it became equal to EXPECTED"
2025-07-02 05:45:26.949 #     (i.e. how to "REVERT" actual back to expected).
2025-07-02 05:45:26.961 # In order to see such result, we have to specify 'right_lines' to the 1st argument that is passed to ndiff().
2025-07-02 05:45:26.973 # ::: NB :::
2025-07-02 05:45:26.986 # We assume that all tests are written so that ACTUAL output is left side in 'assert' statement and EXPECTED
2025-07-02 05:45:26.998 # is right side, e.g: assert act.clean_stdout == act.clean_expected_stdout
2025-07-02 05:45:27.007 # This requirement is CRUCIAL if we use ndiff() instead of default pytest comparison method!
2025-07-02 05:45:27.013 #
2025-07-02 05:45:27.026 >           return list(ndiff(right_lines, left_lines))
2025-07-02 05:45:27.036
2025-07-02 05:45:27.044 src/firebird/qa/plugin.py:608:
2025-07-02 05:45:27.051 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:27.064
2025-07-02 05:45:27.075 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:27.083 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:27.094 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:27.101
2025-07-02 05:45:27.109 def compare(self, a, b):
2025-07-02 05:45:27.116 r"""
2025-07-02 05:45:27.122 Compare two sequences of lines; generate the resulting delta.
2025-07-02 05:45:27.129
2025-07-02 05:45:27.135 Each sequence must contain individual single-line strings ending with
2025-07-02 05:45:27.146 newlines. Such sequences can be obtained from the `readlines()` method
2025-07-02 05:45:27.156 of file-like objects.  The delta generated also consists of newline-
2025-07-02 05:45:27.165 terminated strings, ready to be printed as-is via the writelines()
2025-07-02 05:45:27.173 method of a file-like object.
2025-07-02 05:45:27.179
2025-07-02 05:45:27.185 Example:
2025-07-02 05:45:27.190
2025-07-02 05:45:27.196 >>> print(''.join(Differ().compare('one\ntwo\nthree\n'.splitlines(True),
2025-07-02 05:45:27.204 ...                                'ore\ntree\nemu\n'.splitlines(True))),
2025-07-02 05:45:27.217 ...       end="")
2025-07-02 05:45:27.227 - one
2025-07-02 05:45:27.248 + ore
2025-07-02 05:45:27.274 - two
2025-07-02 05:45:27.286 - three
2025-07-02 05:45:27.308 + tree
2025-07-02 05:45:27.314 + emu
2025-07-02 05:45:27.322 """
2025-07-02 05:45:27.333
2025-07-02 05:45:27.343 cruncher = SequenceMatcher(self.linejunk, a, b)
2025-07-02 05:45:27.352 for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
2025-07-02 05:45:27.365 if tag == 'replace':
2025-07-02 05:45:27.374 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:27.382 elif tag == 'delete':
2025-07-02 05:45:27.394 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:27.404 elif tag == 'insert':
2025-07-02 05:45:27.412 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:27.418 elif tag == 'equal':
2025-07-02 05:45:27.424 g = self._dump(' ', a, alo, ahi)
2025-07-02 05:45:27.428 else:
2025-07-02 05:45:27.433 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:27.437
2025-07-02 05:45:27.443 >           yield from g
2025-07-02 05:45:27.449
2025-07-02 05:45:27.455 /usr/lib/python3.11/difflib.py:872:
2025-07-02 05:45:27.461 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:27.467
2025-07-02 05:45:27.472 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:27.480 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:27.489 alo = 3, ahi = 1101
2025-07-02 05:45:27.498 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:27.505 blo = 3, bhi = 1101
2025-07-02 05:45:27.511
2025-07-02 05:45:27.518 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:27.524 r"""
2025-07-02 05:45:27.536 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:27.546 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:27.553 synch point, and intraline difference marking is done on the
2025-07-02 05:45:27.560 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:27.568
2025-07-02 05:45:27.576 Example:
2025-07-02 05:45:27.587
2025-07-02 05:45:27.594 >>> d = Differ()
2025-07-02 05:45:27.602 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:27.610 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:27.617 >>> print(''.join(results), end="")
2025-07-02 05:45:27.624 - abcDefghiJkl
2025-07-02 05:45:27.635 + abcdefGhijkl
2025-07-02 05:45:27.652 """
2025-07-02 05:45:27.659
2025-07-02 05:45:27.666 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:27.673 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:27.679 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:27.685 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:27.692 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:27.699
2025-07-02 05:45:27.707 # search for the pair that matches best without being identical
2025-07-02 05:45:27.714 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:27.720 # on junk -- unless we have to)
2025-07-02 05:45:27.731 for j in range(blo, bhi):
2025-07-02 05:45:27.741 bj = b[j]
2025-07-02 05:45:27.749 cruncher.set_seq2(bj)
2025-07-02 05:45:27.755 for i in range(alo, ahi):
2025-07-02 05:45:27.760 ai = a[i]
2025-07-02 05:45:27.765 if ai == bj:
2025-07-02 05:45:27.770 if eqi is None:
2025-07-02 05:45:27.775 eqi, eqj = i, j
2025-07-02 05:45:27.781 continue
2025-07-02 05:45:27.787 cruncher.set_seq1(ai)
2025-07-02 05:45:27.794 # computing similarity is expensive, so use the quick
2025-07-02 05:45:27.800 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:27.807 # compares by a factor of 3.
2025-07-02 05:45:27.815 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:27.823 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:27.830 # of the computation is cached by cruncher
2025-07-02 05:45:27.840 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:27.851 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:27.859 cruncher.ratio() > best_ratio:
2025-07-02 05:45:27.867 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:27.875 if best_ratio < cutoff:
2025-07-02 05:45:27.887 # no non-identical "pretty close" pair
2025-07-02 05:45:27.894 if eqi is None:
2025-07-02 05:45:27.901 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:27.907 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:27.914 return
2025-07-02 05:45:27.921 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:27.928 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:27.935 else:
2025-07-02 05:45:27.941 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:27.947 eqi = None
2025-07-02 05:45:27.953
2025-07-02 05:45:27.960 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:27.966 # identical
2025-07-02 05:45:27.976
2025-07-02 05:45:27.986 # pump out diffs from before the synch point
2025-07-02 05:45:27.993 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:28.002
2025-07-02 05:45:28.008 # do intraline marking on the synch pair
2025-07-02 05:45:28.014 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:28.020 if eqi is None:
2025-07-02 05:45:28.025 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:28.030 atags = btags = ""
2025-07-02 05:45:28.035 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:28.040 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:28.046 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:28.050 if tag == 'replace':
2025-07-02 05:45:28.055 atags += '^' * la
2025-07-02 05:45:28.060 btags += '^' * lb
2025-07-02 05:45:28.065 elif tag == 'delete':
2025-07-02 05:45:28.070 atags += '-' * la
2025-07-02 05:45:28.075 elif tag == 'insert':
2025-07-02 05:45:28.080 btags += '+' * lb
2025-07-02 05:45:28.085 elif tag == 'equal':
2025-07-02 05:45:28.090 atags += ' ' * la
2025-07-02 05:45:28.095 btags += ' ' * lb
2025-07-02 05:45:28.099 else:
2025-07-02 05:45:28.105 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:28.113 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:28.120 else:
2025-07-02 05:45:28.128 # the synch pair is identical
2025-07-02 05:45:28.135 yield '  ' + aelt
2025-07-02 05:45:28.142
2025-07-02 05:45:28.153 # pump out diffs from after the synch point
2025-07-02 05:45:28.164 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:28.172
2025-07-02 05:45:28.178 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:28.184 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:28.190
2025-07-02 05:45:28.196 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:28.206 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:28.217 alo = 4, ahi = 1101
2025-07-02 05:45:28.226 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:28.233 blo = 4, bhi = 1101
2025-07-02 05:45:28.242
2025-07-02 05:45:28.249 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:28.256 g = []
2025-07-02 05:45:28.270 if alo < ahi:
2025-07-02 05:45:28.280 if blo < bhi:
2025-07-02 05:45:28.292 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:28.301 else:
2025-07-02 05:45:28.308 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:28.315 elif blo < bhi:
2025-07-02 05:45:28.322 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:28.333
2025-07-02 05:45:28.344 >       yield from g
2025-07-02 05:45:28.352
2025-07-02 05:45:28.365 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:28.380 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:28.393
2025-07-02 05:45:28.406 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:28.417 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:28.429 alo = 4, ahi = 1101
2025-07-02 05:45:28.440 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:28.449 blo = 4, bhi = 1101
2025-07-02 05:45:28.459
2025-07-02 05:45:28.471 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:28.480 r"""
2025-07-02 05:45:28.488 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:28.496 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:28.503 synch point, and intraline difference marking is done on the
2025-07-02 05:45:28.510 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:28.522
2025-07-02 05:45:28.532 Example:
2025-07-02 05:45:28.540
2025-07-02 05:45:28.546 >>> d = Differ()
2025-07-02 05:45:28.553 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:28.560 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:28.566 >>> print(''.join(results), end="")
2025-07-02 05:45:28.573 - abcDefghiJkl
2025-07-02 05:45:28.586 + abcdefGhijkl
2025-07-02 05:45:28.607 """
2025-07-02 05:45:28.614
2025-07-02 05:45:28.623 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:28.635 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:28.646 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:28.660 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:28.673 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:28.685
2025-07-02 05:45:28.698 # search for the pair that matches best without being identical
2025-07-02 05:45:28.710 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:28.720 # on junk -- unless we have to)
2025-07-02 05:45:28.733 for j in range(blo, bhi):
2025-07-02 05:45:28.743 bj = b[j]
2025-07-02 05:45:28.751 cruncher.set_seq2(bj)
2025-07-02 05:45:28.759 for i in range(alo, ahi):
2025-07-02 05:45:28.770 ai = a[i]
2025-07-02 05:45:28.780 if ai == bj:
2025-07-02 05:45:28.789 if eqi is None:
2025-07-02 05:45:28.796 eqi, eqj = i, j
2025-07-02 05:45:28.803 continue
2025-07-02 05:45:28.814 cruncher.set_seq1(ai)
2025-07-02 05:45:28.827 # computing similarity is expensive, so use the quick
2025-07-02 05:45:28.835 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:28.841 # compares by a factor of 3.
2025-07-02 05:45:28.846 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:28.852 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:28.861 # of the computation is cached by cruncher
2025-07-02 05:45:28.871 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:28.880 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:28.890 cruncher.ratio() > best_ratio:
2025-07-02 05:45:28.901 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:28.914 if best_ratio < cutoff:
2025-07-02 05:45:28.928 # no non-identical "pretty close" pair
2025-07-02 05:45:28.940 if eqi is None:
2025-07-02 05:45:28.950 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:28.959 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:28.966 return
2025-07-02 05:45:28.979 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:28.986 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:28.992 else:
2025-07-02 05:45:29.000 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:29.007 eqi = None
2025-07-02 05:45:29.013
2025-07-02 05:45:29.026 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:29.036 # identical
2025-07-02 05:45:29.043
2025-07-02 05:45:29.054 # pump out diffs from before the synch point
2025-07-02 05:45:29.062 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:29.069
2025-07-02 05:45:29.076 # do intraline marking on the synch pair
2025-07-02 05:45:29.083 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:29.091 if eqi is None:
2025-07-02 05:45:29.102 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:29.111 atags = btags = ""
2025-07-02 05:45:29.118 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:29.127 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:29.138 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:29.147 if tag == 'replace':
2025-07-02 05:45:29.155 atags += '^' * la
2025-07-02 05:45:29.165 btags += '^' * lb
2025-07-02 05:45:29.176 elif tag == 'delete':
2025-07-02 05:45:29.189 atags += '-' * la
2025-07-02 05:45:29.201 elif tag == 'insert':
2025-07-02 05:45:29.212 btags += '+' * lb
2025-07-02 05:45:29.221 elif tag == 'equal':
2025-07-02 05:45:29.228 atags += ' ' * la
2025-07-02 05:45:29.236 btags += ' ' * lb
2025-07-02 05:45:29.243 else:
2025-07-02 05:45:29.249 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:29.256 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:29.262 else:
2025-07-02 05:45:29.268 # the synch pair is identical
2025-07-02 05:45:29.274 yield '  ' + aelt
2025-07-02 05:45:29.280
2025-07-02 05:45:29.286 # pump out diffs from after the synch point
2025-07-02 05:45:29.297 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:29.307
2025-07-02 05:45:29.318 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:29.329 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:29.337
2025-07-02 05:45:29.343 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:29.350 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:29.357 alo = 5, ahi = 1101
2025-07-02 05:45:29.372 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:29.381 blo = 5, bhi = 1101
2025-07-02 05:45:29.388
2025-07-02 05:45:29.396 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:29.403 g = []
2025-07-02 05:45:29.412 if alo < ahi:
2025-07-02 05:45:29.428 if blo < bhi:
2025-07-02 05:45:29.437 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:29.444 else:
2025-07-02 05:45:29.451 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:29.457 elif blo < bhi:
2025-07-02 05:45:29.463 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:29.468
2025-07-02 05:45:29.474 >       yield from g
2025-07-02 05:45:29.480
2025-07-02 05:45:29.489 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:29.500 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:29.510
2025-07-02 05:45:29.521 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:29.531 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:29.545 alo = 5, ahi = 1101
2025-07-02 05:45:29.556 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:29.564 blo = 5, bhi = 1101
2025-07-02 05:45:29.571
2025-07-02 05:45:29.578 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:29.584 r"""
2025-07-02 05:45:29.590 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:29.597 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:29.610 synch point, and intraline difference marking is done on the
2025-07-02 05:45:29.620 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:29.628
2025-07-02 05:45:29.636 Example:
2025-07-02 05:45:29.642
2025-07-02 05:45:29.648 >>> d = Differ()
2025-07-02 05:45:29.654 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:29.665 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:29.679 >>> print(''.join(results), end="")
2025-07-02 05:45:29.691 - abcDefghiJkl
2025-07-02 05:45:29.710 + abcdefGhijkl
2025-07-02 05:45:29.731 """
2025-07-02 05:45:29.744
2025-07-02 05:45:29.755 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:29.764 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:29.772 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:29.781 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:29.793 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:29.804
2025-07-02 05:45:29.812 # search for the pair that matches best without being identical
2025-07-02 05:45:29.819 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:29.825 # on junk -- unless we have to)
2025-07-02 05:45:29.831 for j in range(blo, bhi):
2025-07-02 05:45:29.843 bj = b[j]
2025-07-02 05:45:29.853 cruncher.set_seq2(bj)
2025-07-02 05:45:29.866 for i in range(alo, ahi):
2025-07-02 05:45:29.877 ai = a[i]
2025-07-02 05:45:29.885 if ai == bj:
2025-07-02 05:45:29.895 if eqi is None:
2025-07-02 05:45:29.905 eqi, eqj = i, j
2025-07-02 05:45:29.916 continue
2025-07-02 05:45:29.925 cruncher.set_seq1(ai)
2025-07-02 05:45:29.936 # computing similarity is expensive, so use the quick
2025-07-02 05:45:29.947 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:29.957 # compares by a factor of 3.
2025-07-02 05:45:29.964 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:29.971 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:29.983 # of the computation is cached by cruncher
2025-07-02 05:45:29.994 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:30.008 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:30.017 cruncher.ratio() > best_ratio:
2025-07-02 05:45:30.028 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:30.038 if best_ratio < cutoff:
2025-07-02 05:45:30.045 # no non-identical "pretty close" pair
2025-07-02 05:45:30.051 if eqi is None:
2025-07-02 05:45:30.056 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:30.060 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:30.068 return
2025-07-02 05:45:30.077 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:30.086 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:30.093 else:
2025-07-02 05:45:30.099 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:30.108 eqi = None
2025-07-02 05:45:30.118
2025-07-02 05:45:30.126 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:30.134 # identical
2025-07-02 05:45:30.145
2025-07-02 05:45:30.155 # pump out diffs from before the synch point
2025-07-02 05:45:30.164 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:30.170
2025-07-02 05:45:30.183 # do intraline marking on the synch pair
2025-07-02 05:45:30.195 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:30.204 if eqi is None:
2025-07-02 05:45:30.212 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:30.219 atags = btags = ""
2025-07-02 05:45:30.226 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:30.237 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:30.249 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:30.257 if tag == 'replace':
2025-07-02 05:45:30.267 atags += '^' * la
2025-07-02 05:45:30.280 btags += '^' * lb
2025-07-02 05:45:30.293 elif tag == 'delete':
2025-07-02 05:45:30.300 atags += '-' * la
2025-07-02 05:45:30.307 elif tag == 'insert':
2025-07-02 05:45:30.312 btags += '+' * lb
2025-07-02 05:45:30.319 elif tag == 'equal':
2025-07-02 05:45:30.328 atags += ' ' * la
2025-07-02 05:45:30.337 btags += ' ' * lb
2025-07-02 05:45:30.345 else:
2025-07-02 05:45:30.358 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:30.369 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:30.377 else:
2025-07-02 05:45:30.384 # the synch pair is identical
2025-07-02 05:45:30.391 yield '  ' + aelt
2025-07-02 05:45:30.397
2025-07-02 05:45:30.404 # pump out diffs from after the synch point
2025-07-02 05:45:30.411 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:30.418
2025-07-02 05:45:30.429 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:30.438 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:30.448
2025-07-02 05:45:30.459 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:30.468 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:30.474 alo = 6, ahi = 1101
2025-07-02 05:45:30.481 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:30.487 blo = 6, bhi = 1101
2025-07-02 05:45:30.494
2025-07-02 05:45:30.506 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:30.516 g = []
2025-07-02 05:45:30.523 if alo < ahi:
2025-07-02 05:45:30.530 if blo < bhi:
2025-07-02 05:45:30.538 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:30.544 else:
2025-07-02 05:45:30.550 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:30.556 elif blo < bhi:
2025-07-02 05:45:30.564 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:30.571
2025-07-02 05:45:30.578 >       yield from g
2025-07-02 05:45:30.584
2025-07-02 05:45:30.590 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:30.596 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:30.602
2025-07-02 05:45:30.609 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:30.617 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:30.623 alo = 6, ahi = 1101
2025-07-02 05:45:30.632 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:30.638 blo = 6, bhi = 1101
2025-07-02 05:45:30.643
2025-07-02 05:45:30.649 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:30.654 r"""
2025-07-02 05:45:30.660 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:30.667 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:30.673 synch point, and intraline difference marking is done on the
2025-07-02 05:45:30.680 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:30.686
2025-07-02 05:45:30.695 Example:
2025-07-02 05:45:30.704
2025-07-02 05:45:30.712 >>> d = Differ()
2025-07-02 05:45:30.720 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:30.725 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:30.730 >>> print(''.join(results), end="")
2025-07-02 05:45:30.735 - abcDefghiJkl
2025-07-02 05:45:30.745 + abcdefGhijkl
2025-07-02 05:45:30.758 """
2025-07-02 05:45:30.767
2025-07-02 05:45:30.779 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:30.788 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:30.795 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:30.801 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:30.806 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:30.810
2025-07-02 05:45:30.815 # search for the pair that matches best without being identical
2025-07-02 05:45:30.820 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:30.825 # on junk -- unless we have to)
2025-07-02 05:45:30.831 for j in range(blo, bhi):
2025-07-02 05:45:30.837 bj = b[j]
2025-07-02 05:45:30.843 cruncher.set_seq2(bj)
2025-07-02 05:45:30.850 for i in range(alo, ahi):
2025-07-02 05:45:30.860 ai = a[i]
2025-07-02 05:45:30.868 if ai == bj:
2025-07-02 05:45:30.878 if eqi is None:
2025-07-02 05:45:30.885 eqi, eqj = i, j
2025-07-02 05:45:30.892 continue
2025-07-02 05:45:30.899 cruncher.set_seq1(ai)
2025-07-02 05:45:30.907 # computing similarity is expensive, so use the quick
2025-07-02 05:45:30.916 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:30.925 # compares by a factor of 3.
2025-07-02 05:45:30.933 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:30.939 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:30.946 # of the computation is cached by cruncher
2025-07-02 05:45:30.952 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:30.959 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:30.967 cruncher.ratio() > best_ratio:
2025-07-02 05:45:30.978 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:30.986 if best_ratio < cutoff:
2025-07-02 05:45:30.993 # no non-identical "pretty close" pair
2025-07-02 05:45:30.999 if eqi is None:
2025-07-02 05:45:31.006 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:31.013 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:31.019 return
2025-07-02 05:45:31.025 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:31.031 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:31.037 else:
2025-07-02 05:45:31.043 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:31.050 eqi = None
2025-07-02 05:45:31.056
2025-07-02 05:45:31.062 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:31.069 # identical
2025-07-02 05:45:31.075
2025-07-02 05:45:31.079 # pump out diffs from before the synch point
2025-07-02 05:45:31.085 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:31.090
2025-07-02 05:45:31.103 # do intraline marking on the synch pair
2025-07-02 05:45:31.114 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:31.123 if eqi is None:
2025-07-02 05:45:31.129 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:31.135 atags = btags = ""
2025-07-02 05:45:31.141 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:31.153 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:31.161 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:31.173 if tag == 'replace':
2025-07-02 05:45:31.182 atags += '^' * la
2025-07-02 05:45:31.196 btags += '^' * lb
2025-07-02 05:45:31.206 elif tag == 'delete':
2025-07-02 05:45:31.218 atags += '-' * la
2025-07-02 05:45:31.229 elif tag == 'insert':
2025-07-02 05:45:31.241 btags += '+' * lb
2025-07-02 05:45:31.257 elif tag == 'equal':
2025-07-02 05:45:31.267 atags += ' ' * la
2025-07-02 05:45:31.275 btags += ' ' * lb
2025-07-02 05:45:31.285 else:
2025-07-02 05:45:31.296 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:31.307 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:31.315 else:
2025-07-02 05:45:31.323 # the synch pair is identical
2025-07-02 05:45:31.334 yield '  ' + aelt
2025-07-02 05:45:31.345
2025-07-02 05:45:31.358 # pump out diffs from after the synch point
2025-07-02 05:45:31.370 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:31.381
2025-07-02 05:45:31.395 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:31.408 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:31.417
2025-07-02 05:45:31.424 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:31.430 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:31.441 alo = 7, ahi = 1101
2025-07-02 05:45:31.451 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:31.463 blo = 7, bhi = 1101
2025-07-02 05:45:31.477
2025-07-02 05:45:31.487 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:31.497 g = []
2025-07-02 05:45:31.510 if alo < ahi:
2025-07-02 05:45:31.522 if blo < bhi:
2025-07-02 05:45:31.532 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:31.540 else:
2025-07-02 05:45:31.547 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:31.554 elif blo < bhi:
2025-07-02 05:45:31.560 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:31.567
2025-07-02 05:45:31.578 >       yield from g
2025-07-02 05:45:31.588
2025-07-02 05:45:31.596 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:31.604 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:31.610
2025-07-02 05:45:31.616 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:31.628 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:31.639 alo = 7, ahi = 1101
2025-07-02 05:45:31.648 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:31.655 blo = 7, bhi = 1101
2025-07-02 05:45:31.663
2025-07-02 05:45:31.675 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:31.683 r"""
2025-07-02 05:45:31.692 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:31.705 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:31.715 synch point, and intraline difference marking is done on the
2025-07-02 05:45:31.725 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:31.735
2025-07-02 05:45:31.743 Example:
2025-07-02 05:45:31.751
2025-07-02 05:45:31.761 >>> d = Differ()
2025-07-02 05:45:31.769 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:31.776 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:31.783 >>> print(''.join(results), end="")
2025-07-02 05:45:31.795 - abcDefghiJkl
2025-07-02 05:45:31.813 + abcdefGhijkl
2025-07-02 05:45:31.825 """
2025-07-02 05:45:31.832
2025-07-02 05:45:31.840 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:31.848 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:31.855 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:31.864 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:31.872 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:31.879
2025-07-02 05:45:31.885 # search for the pair that matches best without being identical
2025-07-02 05:45:31.891 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:31.900 # on junk -- unless we have to)
2025-07-02 05:45:31.911 for j in range(blo, bhi):
2025-07-02 05:45:31.921 bj = b[j]
2025-07-02 05:45:31.928 cruncher.set_seq2(bj)
2025-07-02 05:45:31.935 for i in range(alo, ahi):
2025-07-02 05:45:31.941 ai = a[i]
2025-07-02 05:45:31.948 if ai == bj:
2025-07-02 05:45:31.954 if eqi is None:
2025-07-02 05:45:31.961 eqi, eqj = i, j
2025-07-02 05:45:31.968 continue
2025-07-02 05:45:31.980 cruncher.set_seq1(ai)
2025-07-02 05:45:31.991 # computing similarity is expensive, so use the quick
2025-07-02 05:45:32.002 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:32.011 # compares by a factor of 3.
2025-07-02 05:45:32.017 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:32.024 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:32.029 # of the computation is cached by cruncher
2025-07-02 05:45:32.038 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:32.045 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:32.051 cruncher.ratio() > best_ratio:
2025-07-02 05:45:32.059 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:32.068 if best_ratio < cutoff:
2025-07-02 05:45:32.077 # no non-identical "pretty close" pair
2025-07-02 05:45:32.085 if eqi is None:
2025-07-02 05:45:32.093 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:32.101 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:32.109 return
2025-07-02 05:45:32.121 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:32.132 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:32.139 else:
2025-07-02 05:45:32.146 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:32.152 eqi = None
2025-07-02 05:45:32.158
2025-07-02 05:45:32.165 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:32.172 # identical
2025-07-02 05:45:32.179
2025-07-02 05:45:32.186 # pump out diffs from before the synch point
2025-07-02 05:45:32.195 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:32.202
2025-07-02 05:45:32.214 # do intraline marking on the synch pair
2025-07-02 05:45:32.223 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:32.230 if eqi is None:
2025-07-02 05:45:32.237 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:32.243 atags = btags = ""
2025-07-02 05:45:32.251 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:32.266 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:32.277 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:32.287 if tag == 'replace':
2025-07-02 05:45:32.296 atags += '^' * la
2025-07-02 05:45:32.308 btags += '^' * lb
2025-07-02 05:45:32.322 elif tag == 'delete':
2025-07-02 05:45:32.336 atags += '-' * la
2025-07-02 05:45:32.347 elif tag == 'insert':
2025-07-02 05:45:32.358 btags += '+' * lb
2025-07-02 05:45:32.367 elif tag == 'equal':
2025-07-02 05:45:32.375 atags += ' ' * la
2025-07-02 05:45:32.382 btags += ' ' * lb
2025-07-02 05:45:32.387 else:
2025-07-02 05:45:32.394 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:32.402 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:32.409 else:
2025-07-02 05:45:32.419 # the synch pair is identical
2025-07-02 05:45:32.427 yield '  ' + aelt
2025-07-02 05:45:32.433
2025-07-02 05:45:32.445 # pump out diffs from after the synch point
2025-07-02 05:45:32.455 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:32.464
2025-07-02 05:45:32.470 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:32.478 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:32.484
2025-07-02 05:45:32.493 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:32.507 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:32.518 alo = 8, ahi = 1101
2025-07-02 05:45:32.531 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:32.540 blo = 8, bhi = 1101
2025-07-02 05:45:32.546
2025-07-02 05:45:32.552 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:32.558 g = []
2025-07-02 05:45:32.564 if alo < ahi:
2025-07-02 05:45:32.571 if blo < bhi:
2025-07-02 05:45:32.580 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:32.587 else:
2025-07-02 05:45:32.593 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:32.606 elif blo < bhi:
2025-07-02 05:45:32.618 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:32.627
2025-07-02 05:45:32.633 >       yield from g
2025-07-02 05:45:32.639
2025-07-02 05:45:32.649 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:32.662 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:32.674
2025-07-02 05:45:32.684 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:32.695 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:32.702 alo = 8, ahi = 1101
2025-07-02 05:45:32.710 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:32.721 blo = 8, bhi = 1101
2025-07-02 05:45:32.733
2025-07-02 05:45:32.741 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:32.748 r"""
2025-07-02 05:45:32.755 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:32.761 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:32.766 synch point, and intraline difference marking is done on the
2025-07-02 05:45:32.771 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:32.776
2025-07-02 05:45:32.780 Example:
2025-07-02 05:45:32.785
2025-07-02 05:45:32.790 >>> d = Differ()
2025-07-02 05:45:32.794 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:32.799 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:32.803 >>> print(''.join(results), end="")
2025-07-02 05:45:32.808 - abcDefghiJkl
2025-07-02 05:45:32.817 + abcdefGhijkl
2025-07-02 05:45:32.828 """
2025-07-02 05:45:32.837
2025-07-02 05:45:32.849 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:32.857 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:32.864 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:32.871 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:32.883 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:32.890
2025-07-02 05:45:32.895 # search for the pair that matches best without being identical
2025-07-02 05:45:32.901 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:32.906 # on junk -- unless we have to)
2025-07-02 05:45:32.912 for j in range(blo, bhi):
2025-07-02 05:45:32.918 bj = b[j]
2025-07-02 05:45:32.924 cruncher.set_seq2(bj)
2025-07-02 05:45:32.931 for i in range(alo, ahi):
2025-07-02 05:45:32.940 ai = a[i]
2025-07-02 05:45:32.949 if ai == bj:
2025-07-02 05:45:32.957 if eqi is None:
2025-07-02 05:45:32.964 eqi, eqj = i, j
2025-07-02 05:45:32.970 continue
2025-07-02 05:45:32.976 cruncher.set_seq1(ai)
2025-07-02 05:45:32.982 # computing similarity is expensive, so use the quick
2025-07-02 05:45:32.993 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:33.003 # compares by a factor of 3.
2025-07-02 05:45:33.012 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:33.020 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:33.026 # of the computation is cached by cruncher
2025-07-02 05:45:33.032 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:33.038 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:33.045 cruncher.ratio() > best_ratio:
2025-07-02 05:45:33.051 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:33.061 if best_ratio < cutoff:
2025-07-02 05:45:33.071 # no non-identical "pretty close" pair
2025-07-02 05:45:33.079 if eqi is None:
2025-07-02 05:45:33.087 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:33.094 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:33.101 return
2025-07-02 05:45:33.109 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:33.115 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:33.123 else:
2025-07-02 05:45:33.135 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:33.149 eqi = None
2025-07-02 05:45:33.159
2025-07-02 05:45:33.169 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:33.179 # identical
2025-07-02 05:45:33.189
2025-07-02 05:45:33.197 # pump out diffs from before the synch point
2025-07-02 05:45:33.209 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:33.218
2025-07-02 05:45:33.230 # do intraline marking on the synch pair
2025-07-02 05:45:33.241 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:33.254 if eqi is None:
2025-07-02 05:45:33.267 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:33.279 atags = btags = ""
2025-07-02 05:45:33.291 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:33.303 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:33.314 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:33.323 if tag == 'replace':
2025-07-02 05:45:33.330 atags += '^' * la
2025-07-02 05:45:33.339 btags += '^' * lb
2025-07-02 05:45:33.352 elif tag == 'delete':
2025-07-02 05:45:33.360 atags += '-' * la
2025-07-02 05:45:33.369 elif tag == 'insert':
2025-07-02 05:45:33.382 btags += '+' * lb
2025-07-02 05:45:33.394 elif tag == 'equal':
2025-07-02 05:45:33.403 atags += ' ' * la
2025-07-02 05:45:33.416 btags += ' ' * lb
2025-07-02 05:45:33.428 else:
2025-07-02 05:45:33.442 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:33.450 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:33.462 else:
2025-07-02 05:45:33.472 # the synch pair is identical
2025-07-02 05:45:33.485 yield '  ' + aelt
2025-07-02 05:45:33.497
2025-07-02 05:45:33.508 # pump out diffs from after the synch point
2025-07-02 05:45:33.517 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:33.524
2025-07-02 05:45:33.531 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:33.539 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:33.550
2025-07-02 05:45:33.558 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:33.565 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:33.571 alo = 9, ahi = 1101
2025-07-02 05:45:33.586 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:33.597 blo = 9, bhi = 1101
2025-07-02 05:45:33.605
2025-07-02 05:45:33.616 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:33.630 g = []
2025-07-02 05:45:33.641 if alo < ahi:
2025-07-02 05:45:33.653 if blo < bhi:
2025-07-02 05:45:33.666 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:33.678 else:
2025-07-02 05:45:33.691 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:33.697 elif blo < bhi:
2025-07-02 05:45:33.703 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:33.715
2025-07-02 05:45:33.728 >       yield from g
2025-07-02 05:45:33.736
2025-07-02 05:45:33.741 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:33.750 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:33.761
2025-07-02 05:45:33.770 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:33.782 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:33.790 alo = 9, ahi = 1101
2025-07-02 05:45:33.798 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:33.805 blo = 9, bhi = 1101
2025-07-02 05:45:33.812
2025-07-02 05:45:33.820 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:33.830 r"""
2025-07-02 05:45:33.843 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:33.853 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:33.862 synch point, and intraline difference marking is done on the
2025-07-02 05:45:33.874 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:33.888
2025-07-02 05:45:33.900 Example:
2025-07-02 05:45:33.914
2025-07-02 05:45:33.929 >>> d = Differ()
2025-07-02 05:45:33.941 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:33.952 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:33.960 >>> print(''.join(results), end="")
2025-07-02 05:45:33.968 - abcDefghiJkl
2025-07-02 05:45:33.987 + abcdefGhijkl
2025-07-02 05:45:34.010 """
2025-07-02 05:45:34.019
2025-07-02 05:45:34.033 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:34.046 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:34.057 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:34.069 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:34.080 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:34.092
2025-07-02 05:45:34.102 # search for the pair that matches best without being identical
2025-07-02 05:45:34.112 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:34.120 # on junk -- unless we have to)
2025-07-02 05:45:34.126 for j in range(blo, bhi):
2025-07-02 05:45:34.132 bj = b[j]
2025-07-02 05:45:34.138 cruncher.set_seq2(bj)
2025-07-02 05:45:34.145 for i in range(alo, ahi):
2025-07-02 05:45:34.152 ai = a[i]
2025-07-02 05:45:34.164 if ai == bj:
2025-07-02 05:45:34.172 if eqi is None:
2025-07-02 05:45:34.185 eqi, eqj = i, j
2025-07-02 05:45:34.196 continue
2025-07-02 05:45:34.206 cruncher.set_seq1(ai)
2025-07-02 05:45:34.214 # computing similarity is expensive, so use the quick
2025-07-02 05:45:34.220 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:34.227 # compares by a factor of 3.
2025-07-02 05:45:34.234 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:34.242 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:34.248 # of the computation is cached by cruncher
2025-07-02 05:45:34.254 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:34.261 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:34.268 cruncher.ratio() > best_ratio:
2025-07-02 05:45:34.276 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:34.284 if best_ratio < cutoff:
2025-07-02 05:45:34.290 # no non-identical "pretty close" pair
2025-07-02 05:45:34.297 if eqi is None:
2025-07-02 05:45:34.303 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:34.308 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:34.313 return
2025-07-02 05:45:34.318 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:34.323 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:34.329 else:
2025-07-02 05:45:34.337 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:34.346 eqi = None
2025-07-02 05:45:34.359
2025-07-02 05:45:34.369 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:34.376 # identical
2025-07-02 05:45:34.384
2025-07-02 05:45:34.393 # pump out diffs from before the synch point
2025-07-02 05:45:34.400 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:34.407
2025-07-02 05:45:34.413 # do intraline marking on the synch pair
2025-07-02 05:45:34.419 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:34.433 if eqi is None:
2025-07-02 05:45:34.442 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:34.449 atags = btags = ""
2025-07-02 05:45:34.458 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:34.468 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:34.478 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:34.487 if tag == 'replace':
2025-07-02 05:45:34.495 atags += '^' * la
2025-07-02 05:45:34.504 btags += '^' * lb
2025-07-02 05:45:34.515 elif tag == 'delete':
2025-07-02 05:45:34.525 atags += '-' * la
2025-07-02 05:45:34.532 elif tag == 'insert':
2025-07-02 05:45:34.540 btags += '+' * lb
2025-07-02 05:45:34.546 elif tag == 'equal':
2025-07-02 05:45:34.553 atags += ' ' * la
2025-07-02 05:45:34.559 btags += ' ' * lb
2025-07-02 05:45:34.565 else:
2025-07-02 05:45:34.570 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:34.581 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:34.590 else:
2025-07-02 05:45:34.598 # the synch pair is identical
2025-07-02 05:45:34.610 yield '  ' + aelt
2025-07-02 05:45:34.619
2025-07-02 05:45:34.626 # pump out diffs from after the synch point
2025-07-02 05:45:34.633 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:34.640
2025-07-02 05:45:34.647 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:34.653 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:34.660
2025-07-02 05:45:34.666 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:34.674 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:34.685 alo = 10, ahi = 1101
2025-07-02 05:45:34.695 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:34.703 blo = 10, bhi = 1101
2025-07-02 05:45:34.709
2025-07-02 05:45:34.720 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:34.728 g = []
2025-07-02 05:45:34.735 if alo < ahi:
2025-07-02 05:45:34.741 if blo < bhi:
2025-07-02 05:45:34.747 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:34.753 else:
2025-07-02 05:45:34.760 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:34.766 elif blo < bhi:
2025-07-02 05:45:34.773 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:34.780
2025-07-02 05:45:34.787 >       yield from g
2025-07-02 05:45:34.794
2025-07-02 05:45:34.804 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:34.814 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:34.823
2025-07-02 05:45:34.833 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:34.841 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:34.848 alo = 10, ahi = 1101
2025-07-02 05:45:34.859 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:34.871 blo = 10, bhi = 1101
2025-07-02 05:45:34.879
2025-07-02 05:45:34.886 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:34.894 r"""
2025-07-02 05:45:34.901 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:34.908 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:34.915 synch point, and intraline difference marking is done on the
2025-07-02 05:45:34.922 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:34.930
2025-07-02 05:45:34.941 Example:
2025-07-02 05:45:34.949
2025-07-02 05:45:34.957 >>> d = Differ()
2025-07-02 05:45:34.963 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:34.968 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:34.973 >>> print(''.join(results), end="")
2025-07-02 05:45:34.978 - abcDefghiJkl
2025-07-02 05:45:34.989 + abcdefGhijkl
2025-07-02 05:45:35.008 """
2025-07-02 05:45:35.019
2025-07-02 05:45:35.032 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:35.043 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:35.051 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:35.059 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:35.066 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:35.073
2025-07-02 05:45:35.086 # search for the pair that matches best without being identical
2025-07-02 05:45:35.095 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:35.102 # on junk -- unless we have to)
2025-07-02 05:45:35.110 for j in range(blo, bhi):
2025-07-02 05:45:35.117 bj = b[j]
2025-07-02 05:45:35.124 cruncher.set_seq2(bj)
2025-07-02 05:45:35.135 for i in range(alo, ahi):
2025-07-02 05:45:35.144 ai = a[i]
2025-07-02 05:45:35.151 if ai == bj:
2025-07-02 05:45:35.159 if eqi is None:
2025-07-02 05:45:35.165 eqi, eqj = i, j
2025-07-02 05:45:35.170 continue
2025-07-02 05:45:35.176 cruncher.set_seq1(ai)
2025-07-02 05:45:35.183 # computing similarity is expensive, so use the quick
2025-07-02 05:45:35.190 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:35.200 # compares by a factor of 3.
2025-07-02 05:45:35.209 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:35.216 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:35.226 # of the computation is cached by cruncher
2025-07-02 05:45:35.238 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:35.250 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:35.260 cruncher.ratio() > best_ratio:
2025-07-02 05:45:35.268 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:35.274 if best_ratio < cutoff:
2025-07-02 05:45:35.281 # no non-identical "pretty close" pair
2025-07-02 05:45:35.286 if eqi is None:
2025-07-02 05:45:35.291 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:35.297 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:35.304 return
2025-07-02 05:45:35.310 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:35.317 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:35.324 else:
2025-07-02 05:45:35.330 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:35.336 eqi = None
2025-07-02 05:45:35.343
2025-07-02 05:45:35.353 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:35.361 # identical
2025-07-02 05:45:35.368
2025-07-02 05:45:35.374 # pump out diffs from before the synch point
2025-07-02 05:45:35.384 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:35.394
2025-07-02 05:45:35.402 # do intraline marking on the synch pair
2025-07-02 05:45:35.412 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:35.423 if eqi is None:
2025-07-02 05:45:35.435 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:35.446 atags = btags = ""
2025-07-02 05:45:35.460 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:35.469 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:35.479 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:35.492 if tag == 'replace':
2025-07-02 05:45:35.504 atags += '^' * la
2025-07-02 05:45:35.516 btags += '^' * lb
2025-07-02 05:45:35.525 elif tag == 'delete':
2025-07-02 05:45:35.539 atags += '-' * la
2025-07-02 05:45:35.549 elif tag == 'insert':
2025-07-02 05:45:35.558 btags += '+' * lb
2025-07-02 05:45:35.572 elif tag == 'equal':
2025-07-02 05:45:35.581 atags += ' ' * la
2025-07-02 05:45:35.589 btags += ' ' * lb
2025-07-02 05:45:35.601 else:
2025-07-02 05:45:35.613 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:35.626 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:35.636 else:
2025-07-02 05:45:35.645 # the synch pair is identical
2025-07-02 05:45:35.651 yield '  ' + aelt
2025-07-02 05:45:35.658
2025-07-02 05:45:35.665 # pump out diffs from after the synch point
2025-07-02 05:45:35.671 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:35.677
2025-07-02 05:45:35.683 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:35.690 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:35.696
2025-07-02 05:45:35.703 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:35.716 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:35.725 alo = 11, ahi = 1101
2025-07-02 05:45:35.736 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:35.749 blo = 11, bhi = 1101
2025-07-02 05:45:35.759
2025-07-02 05:45:35.772 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:35.786 g = []
2025-07-02 05:45:35.799 if alo < ahi:
2025-07-02 05:45:35.809 if blo < bhi:
2025-07-02 05:45:35.821 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:35.833 else:
2025-07-02 05:45:35.841 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:35.853 elif blo < bhi:
2025-07-02 05:45:35.863 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:35.872
2025-07-02 05:45:35.879 >       yield from g
2025-07-02 05:45:35.886
2025-07-02 05:45:35.898 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:35.910 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:35.919
2025-07-02 05:45:35.932 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:35.944 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:35.955 alo = 11, ahi = 1101
2025-07-02 05:45:35.965 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:35.976 blo = 11, bhi = 1101
2025-07-02 05:45:35.986
2025-07-02 05:45:35.995 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:36.007 r"""
2025-07-02 05:45:36.020 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:36.029 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:36.041 synch point, and intraline difference marking is done on the
2025-07-02 05:45:36.051 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:36.060
2025-07-02 05:45:36.068 Example:
2025-07-02 05:45:36.075
2025-07-02 05:45:36.081 >>> d = Differ()
2025-07-02 05:45:36.087 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:36.094 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:36.105 >>> print(''.join(results), end="")
2025-07-02 05:45:36.115 - abcDefghiJkl
2025-07-02 05:45:36.137 + abcdefGhijkl
2025-07-02 05:45:36.152 """
2025-07-02 05:45:36.160
2025-07-02 05:45:36.171 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:36.184 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:36.199 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:36.210 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:36.218 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:36.225
2025-07-02 05:45:36.232 # search for the pair that matches best without being identical
2025-07-02 05:45:36.239 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:36.251 # on junk -- unless we have to)
2025-07-02 05:45:36.261 for j in range(blo, bhi):
2025-07-02 05:45:36.272 bj = b[j]
2025-07-02 05:45:36.283 cruncher.set_seq2(bj)
2025-07-02 05:45:36.295 for i in range(alo, ahi):
2025-07-02 05:45:36.307 ai = a[i]
2025-07-02 05:45:36.316 if ai == bj:
2025-07-02 05:45:36.324 if eqi is None:
2025-07-02 05:45:36.331 eqi, eqj = i, j
2025-07-02 05:45:36.339 continue
2025-07-02 05:45:36.351 cruncher.set_seq1(ai)
2025-07-02 05:45:36.359 # computing similarity is expensive, so use the quick
2025-07-02 05:45:36.367 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:36.376 # compares by a factor of 3.
2025-07-02 05:45:36.386 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:36.395 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:36.404 # of the computation is cached by cruncher
2025-07-02 05:45:36.412 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:36.418 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:36.429 cruncher.ratio() > best_ratio:
2025-07-02 05:45:36.436 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:36.442 if best_ratio < cutoff:
2025-07-02 05:45:36.448 # no non-identical "pretty close" pair
2025-07-02 05:45:36.458 if eqi is None:
2025-07-02 05:45:36.467 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:36.475 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:36.482 return
2025-07-02 05:45:36.494 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:36.503 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:36.513 else:
2025-07-02 05:45:36.524 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:36.537 eqi = None
2025-07-02 05:45:36.549
2025-07-02 05:45:36.559 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:36.567 # identical
2025-07-02 05:45:36.574
2025-07-02 05:45:36.586 # pump out diffs from before the synch point
2025-07-02 05:45:36.596 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:36.608
2025-07-02 05:45:36.620 # do intraline marking on the synch pair
2025-07-02 05:45:36.631 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:36.641 if eqi is None:
2025-07-02 05:45:36.653 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:36.666 atags = btags = ""
2025-07-02 05:45:36.678 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:36.690 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:36.703 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:36.714 if tag == 'replace':
2025-07-02 05:45:36.727 atags += '^' * la
2025-07-02 05:45:36.741 btags += '^' * lb
2025-07-02 05:45:36.753 elif tag == 'delete':
2025-07-02 05:45:36.761 atags += '-' * la
2025-07-02 05:45:36.775 elif tag == 'insert':
2025-07-02 05:45:36.789 btags += '+' * lb
2025-07-02 05:45:36.798 elif tag == 'equal':
2025-07-02 05:45:36.811 atags += ' ' * la
2025-07-02 05:45:36.822 btags += ' ' * lb
2025-07-02 05:45:36.833 else:
2025-07-02 05:45:36.843 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:36.851 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:36.859 else:
2025-07-02 05:45:36.868 # the synch pair is identical
2025-07-02 05:45:36.874 yield '  ' + aelt
2025-07-02 05:45:36.880
2025-07-02 05:45:36.887 # pump out diffs from after the synch point
2025-07-02 05:45:36.895 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:36.906
2025-07-02 05:45:36.918 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:36.930 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:36.942
2025-07-02 05:45:36.951 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:36.960 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:36.966 alo = 12, ahi = 1101
2025-07-02 05:45:36.975 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:36.986 blo = 12, bhi = 1101
2025-07-02 05:45:36.995
2025-07-02 05:45:37.003 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:37.011 g = []
2025-07-02 05:45:37.019 if alo < ahi:
2025-07-02 05:45:37.032 if blo < bhi:
2025-07-02 05:45:37.043 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:37.051 else:
2025-07-02 05:45:37.063 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:37.071 elif blo < bhi:
2025-07-02 05:45:37.079 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:37.085
2025-07-02 05:45:37.090 >       yield from g
2025-07-02 05:45:37.102
2025-07-02 05:45:37.112 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:37.120 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:37.131
2025-07-02 05:45:37.145 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:37.157 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:37.169 alo = 12, ahi = 1101
2025-07-02 05:45:37.178 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:37.186 blo = 12, bhi = 1101
2025-07-02 05:45:37.197
2025-07-02 05:45:37.208 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:37.215 r"""
2025-07-02 05:45:37.224 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:37.231 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:37.238 synch point, and intraline difference marking is done on the
2025-07-02 05:45:37.244 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:37.249
2025-07-02 05:45:37.261 Example:
2025-07-02 05:45:37.272
2025-07-02 05:45:37.281 >>> d = Differ()
2025-07-02 05:45:37.292 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:37.305 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:37.317 >>> print(''.join(results), end="")
2025-07-02 05:45:37.329 - abcDefghiJkl
2025-07-02 05:45:37.348 + abcdefGhijkl
2025-07-02 05:45:37.360 """
2025-07-02 05:45:37.370
2025-07-02 05:45:37.380 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:37.389 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:37.397 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:37.403 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:37.408 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:37.413
2025-07-02 05:45:37.418 # search for the pair that matches best without being identical
2025-07-02 05:45:37.423 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:37.427 # on junk -- unless we have to)
2025-07-02 05:45:37.432 for j in range(blo, bhi):
2025-07-02 05:45:37.437 bj = b[j]
2025-07-02 05:45:37.442 cruncher.set_seq2(bj)
2025-07-02 05:45:37.451 for i in range(alo, ahi):
2025-07-02 05:45:37.463 ai = a[i]
2025-07-02 05:45:37.473 if ai == bj:
2025-07-02 05:45:37.486 if eqi is None:
2025-07-02 05:45:37.496 eqi, eqj = i, j
2025-07-02 05:45:37.504 continue
2025-07-02 05:45:37.513 cruncher.set_seq1(ai)
2025-07-02 05:45:37.523 # computing similarity is expensive, so use the quick
2025-07-02 05:45:37.535 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:37.544 # compares by a factor of 3.
2025-07-02 05:45:37.552 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:37.558 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:37.566 # of the computation is cached by cruncher
2025-07-02 05:45:37.579 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:37.592 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:37.601 cruncher.ratio() > best_ratio:
2025-07-02 05:45:37.609 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:37.617 if best_ratio < cutoff:
2025-07-02 05:45:37.625 # no non-identical "pretty close" pair
2025-07-02 05:45:37.635 if eqi is None:
2025-07-02 05:45:37.646 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:37.660 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:37.669 return
2025-07-02 05:45:37.676 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:37.683 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:37.692 else:
2025-07-02 05:45:37.700 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:37.708 eqi = None
2025-07-02 05:45:37.715
2025-07-02 05:45:37.722 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:37.731 # identical
2025-07-02 05:45:37.743
2025-07-02 05:45:37.752 # pump out diffs from before the synch point
2025-07-02 05:45:37.761 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:37.769
2025-07-02 05:45:37.777 # do intraline marking on the synch pair
2025-07-02 05:45:37.784 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:37.791 if eqi is None:
2025-07-02 05:45:37.805 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:37.815 atags = btags = ""
2025-07-02 05:45:37.826 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:37.834 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:37.843 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:37.852 if tag == 'replace':
2025-07-02 05:45:37.863 atags += '^' * la
2025-07-02 05:45:37.877 btags += '^' * lb
2025-07-02 05:45:37.886 elif tag == 'delete':
2025-07-02 05:45:37.899 atags += '-' * la
2025-07-02 05:45:37.913 elif tag == 'insert':
2025-07-02 05:45:37.925 btags += '+' * lb
2025-07-02 05:45:37.938 elif tag == 'equal':
2025-07-02 05:45:37.949 atags += ' ' * la
2025-07-02 05:45:37.960 btags += ' ' * lb
2025-07-02 05:45:37.973 else:
2025-07-02 05:45:37.983 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:37.991 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:38.001 else:
2025-07-02 05:45:38.012 # the synch pair is identical
2025-07-02 05:45:38.022 yield '  ' + aelt
2025-07-02 05:45:38.031
2025-07-02 05:45:38.041 # pump out diffs from after the synch point
2025-07-02 05:45:38.047 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:38.054
2025-07-02 05:45:38.065 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:38.075 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:38.086
2025-07-02 05:45:38.098 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:38.110 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:38.122 alo = 13, ahi = 1101
2025-07-02 05:45:38.140 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:38.151 blo = 13, bhi = 1101
2025-07-02 05:45:38.158
2025-07-02 05:45:38.166 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:38.174 g = []
2025-07-02 05:45:38.182 if alo < ahi:
2025-07-02 05:45:38.191 if blo < bhi:
2025-07-02 05:45:38.198 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:38.205 else:
2025-07-02 05:45:38.214 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:38.221 elif blo < bhi:
2025-07-02 05:45:38.228 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:38.236
2025-07-02 05:45:38.243 >       yield from g
2025-07-02 05:45:38.251
2025-07-02 05:45:38.264 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:38.275 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:38.284
2025-07-02 05:45:38.291 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:38.298 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:38.304 alo = 13, ahi = 1101
2025-07-02 05:45:38.311 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:38.317 blo = 13, bhi = 1101
2025-07-02 05:45:38.324
2025-07-02 05:45:38.330 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:38.336 r"""
2025-07-02 05:45:38.342 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:38.349 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:38.356 synch point, and intraline difference marking is done on the
2025-07-02 05:45:38.362 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:38.368
2025-07-02 05:45:38.375 Example:
2025-07-02 05:45:38.383
2025-07-02 05:45:38.395 >>> d = Differ()
2025-07-02 05:45:38.404 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:38.412 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:38.419 >>> print(''.join(results), end="")
2025-07-02 05:45:38.427 - abcDefghiJkl
2025-07-02 05:45:38.449 + abcdefGhijkl
2025-07-02 05:45:38.473 """
2025-07-02 05:45:38.484
2025-07-02 05:45:38.495 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:38.503 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:38.516 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:38.527 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:38.535 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:38.543
2025-07-02 05:45:38.551 # search for the pair that matches best without being identical
2025-07-02 05:45:38.562 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:38.574 # on junk -- unless we have to)
2025-07-02 05:45:38.584 for j in range(blo, bhi):
2025-07-02 05:45:38.593 bj = b[j]
2025-07-02 05:45:38.600 cruncher.set_seq2(bj)
2025-07-02 05:45:38.607 for i in range(alo, ahi):
2025-07-02 05:45:38.616 ai = a[i]
2025-07-02 05:45:38.628 if ai == bj:
2025-07-02 05:45:38.639 if eqi is None:
2025-07-02 05:45:38.648 eqi, eqj = i, j
2025-07-02 05:45:38.656 continue
2025-07-02 05:45:38.669 cruncher.set_seq1(ai)
2025-07-02 05:45:38.677 # computing similarity is expensive, so use the quick
2025-07-02 05:45:38.687 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:38.695 # compares by a factor of 3.
2025-07-02 05:45:38.704 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:38.713 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:38.722 # of the computation is cached by cruncher
2025-07-02 05:45:38.730 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:38.743 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:38.752 cruncher.ratio() > best_ratio:
2025-07-02 05:45:38.760 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:38.766 if best_ratio < cutoff:
2025-07-02 05:45:38.771 # no non-identical "pretty close" pair
2025-07-02 05:45:38.777 if eqi is None:
2025-07-02 05:45:38.784 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:38.790 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:38.797 return
2025-07-02 05:45:38.805 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:38.813 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:38.819 else:
2025-07-02 05:45:38.833 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:38.841 eqi = None
2025-07-02 05:45:38.847
2025-07-02 05:45:38.853 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:38.858 # identical
2025-07-02 05:45:38.862
2025-07-02 05:45:38.867 # pump out diffs from before the synch point
2025-07-02 05:45:38.871 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:38.876
2025-07-02 05:45:38.880 # do intraline marking on the synch pair
2025-07-02 05:45:38.885 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:38.889 if eqi is None:
2025-07-02 05:45:38.893 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:38.898 atags = btags = ""
2025-07-02 05:45:38.902 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:38.913 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:38.921 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:38.930 if tag == 'replace':
2025-07-02 05:45:38.938 atags += '^' * la
2025-07-02 05:45:38.946 btags += '^' * lb
2025-07-02 05:45:38.957 elif tag == 'delete':
2025-07-02 05:45:38.968 atags += '-' * la
2025-07-02 05:45:38.976 elif tag == 'insert':
2025-07-02 05:45:38.983 btags += '+' * lb
2025-07-02 05:45:38.991 elif tag == 'equal':
2025-07-02 05:45:38.997 atags += ' ' * la
2025-07-02 05:45:39.003 btags += ' ' * lb
2025-07-02 05:45:39.009 else:
2025-07-02 05:45:39.015 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:39.021 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:39.027 else:
2025-07-02 05:45:39.034 # the synch pair is identical
2025-07-02 05:45:39.039 yield '  ' + aelt
2025-07-02 05:45:39.047
2025-07-02 05:45:39.057 # pump out diffs from after the synch point
2025-07-02 05:45:39.069 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:39.079
2025-07-02 05:45:39.087 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:39.095 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:39.101
2025-07-02 05:45:39.113 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:39.122 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:39.131 alo = 14, ahi = 1101
2025-07-02 05:45:39.139 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:39.146 blo = 14, bhi = 1101
2025-07-02 05:45:39.157
2025-07-02 05:45:39.167 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:39.174 g = []
2025-07-02 05:45:39.185 if alo < ahi:
2025-07-02 05:45:39.195 if blo < bhi:
2025-07-02 05:45:39.203 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:39.210 else:
2025-07-02 05:45:39.219 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:39.232 elif blo < bhi:
2025-07-02 05:45:39.241 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:39.247
2025-07-02 05:45:39.255 >       yield from g
2025-07-02 05:45:39.266
2025-07-02 05:45:39.276 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:39.284 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:39.290
2025-07-02 05:45:39.296 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:39.305 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:39.312 alo = 14, ahi = 1101
2025-07-02 05:45:39.319 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:39.326 blo = 14, bhi = 1101
2025-07-02 05:45:39.335
2025-07-02 05:45:39.344 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:39.354 r"""
2025-07-02 05:45:39.364 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:39.372 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:39.379 synch point, and intraline difference marking is done on the
2025-07-02 05:45:39.393 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:39.404
2025-07-02 05:45:39.414 Example:
2025-07-02 05:45:39.424
2025-07-02 05:45:39.434 >>> d = Differ()
2025-07-02 05:45:39.444 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:39.455 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:39.463 >>> print(''.join(results), end="")
2025-07-02 05:45:39.470 - abcDefghiJkl
2025-07-02 05:45:39.491 + abcdefGhijkl
2025-07-02 05:45:39.512 """
2025-07-02 05:45:39.524
2025-07-02 05:45:39.533 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:39.547 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:39.559 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:39.569 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:39.581 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:39.590
2025-07-02 05:45:39.598 # search for the pair that matches best without being identical
2025-07-02 05:45:39.608 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:39.620 # on junk -- unless we have to)
2025-07-02 05:45:39.628 for j in range(blo, bhi):
2025-07-02 05:45:39.635 bj = b[j]
2025-07-02 05:45:39.642 cruncher.set_seq2(bj)
2025-07-02 05:45:39.651 for i in range(alo, ahi):
2025-07-02 05:45:39.663 ai = a[i]
2025-07-02 05:45:39.675 if ai == bj:
2025-07-02 05:45:39.686 if eqi is None:
2025-07-02 05:45:39.699 eqi, eqj = i, j
2025-07-02 05:45:39.711 continue
2025-07-02 05:45:39.720 cruncher.set_seq1(ai)
2025-07-02 05:45:39.729 # computing similarity is expensive, so use the quick
2025-07-02 05:45:39.737 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:39.745 # compares by a factor of 3.
2025-07-02 05:45:39.757 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:39.766 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:39.775 # of the computation is cached by cruncher
2025-07-02 05:45:39.784 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:39.795 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:39.805 cruncher.ratio() > best_ratio:
2025-07-02 05:45:39.815 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:39.828 if best_ratio < cutoff:
2025-07-02 05:45:39.837 # no non-identical "pretty close" pair
2025-07-02 05:45:39.850 if eqi is None:
2025-07-02 05:45:39.865 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:39.875 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:39.883 return
2025-07-02 05:45:39.891 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:39.898 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:39.904 else:
2025-07-02 05:45:39.911 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:39.916 eqi = None
2025-07-02 05:45:39.923
2025-07-02 05:45:39.933 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:39.943 # identical
2025-07-02 05:45:39.952
2025-07-02 05:45:39.960 # pump out diffs from before the synch point
2025-07-02 05:45:39.967 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:39.976
2025-07-02 05:45:39.989 # do intraline marking on the synch pair
2025-07-02 05:45:39.999 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:40.009 if eqi is None:
2025-07-02 05:45:40.024 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:40.036 atags = btags = ""
2025-07-02 05:45:40.049 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:40.060 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:40.068 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:40.075 if tag == 'replace':
2025-07-02 05:45:40.084 atags += '^' * la
2025-07-02 05:45:40.098 btags += '^' * lb
2025-07-02 05:45:40.108 elif tag == 'delete':
2025-07-02 05:45:40.114 atags += '-' * la
2025-07-02 05:45:40.126 elif tag == 'insert':
2025-07-02 05:45:40.139 btags += '+' * lb
2025-07-02 05:45:40.148 elif tag == 'equal':
2025-07-02 05:45:40.156 atags += ' ' * la
2025-07-02 05:45:40.163 btags += ' ' * lb
2025-07-02 05:45:40.170 else:
2025-07-02 05:45:40.181 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:40.193 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:40.203 else:
2025-07-02 05:45:40.211 # the synch pair is identical
2025-07-02 05:45:40.221 yield '  ' + aelt
2025-07-02 05:45:40.235
2025-07-02 05:45:40.247 # pump out diffs from after the synch point
2025-07-02 05:45:40.260 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:40.269
2025-07-02 05:45:40.278 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:40.288 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:40.295
2025-07-02 05:45:40.301 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:40.308 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:40.314 alo = 15, ahi = 1101
2025-07-02 05:45:40.322 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:40.330 blo = 15, bhi = 1101
2025-07-02 05:45:40.342
2025-07-02 05:45:40.350 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:40.359 g = []
2025-07-02 05:45:40.369 if alo < ahi:
2025-07-02 05:45:40.381 if blo < bhi:
2025-07-02 05:45:40.394 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:40.405 else:
2025-07-02 05:45:40.413 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:40.420 elif blo < bhi:
2025-07-02 05:45:40.426 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:40.437
2025-07-02 05:45:40.446 >       yield from g
2025-07-02 05:45:40.454
2025-07-02 05:45:40.463 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:40.475 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:40.487
2025-07-02 05:45:40.499 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:40.511 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:40.520 alo = 15, ahi = 1101
2025-07-02 05:45:40.528 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:40.534 blo = 15, bhi = 1101
2025-07-02 05:45:40.539
2025-07-02 05:45:40.546 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:40.551 r"""
2025-07-02 05:45:40.556 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:40.563 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:40.571 synch point, and intraline difference marking is done on the
2025-07-02 05:45:40.577 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:40.583
2025-07-02 05:45:40.588 Example:
2025-07-02 05:45:40.600
2025-07-02 05:45:40.611 >>> d = Differ()
2025-07-02 05:45:40.623 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:40.634 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:40.647 >>> print(''.join(results), end="")
2025-07-02 05:45:40.659 - abcDefghiJkl
2025-07-02 05:45:40.676 + abcdefGhijkl
2025-07-02 05:45:40.689 """
2025-07-02 05:45:40.695
2025-07-02 05:45:40.701 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:40.710 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:40.719 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:40.728 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:40.736 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:40.743
2025-07-02 05:45:40.755 # search for the pair that matches best without being identical
2025-07-02 05:45:40.765 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:40.771 # on junk -- unless we have to)
2025-07-02 05:45:40.778 for j in range(blo, bhi):
2025-07-02 05:45:40.788 bj = b[j]
2025-07-02 05:45:40.799 cruncher.set_seq2(bj)
2025-07-02 05:45:40.807 for i in range(alo, ahi):
2025-07-02 05:45:40.814 ai = a[i]
2025-07-02 05:45:40.820 if ai == bj:
2025-07-02 05:45:40.827 if eqi is None:
2025-07-02 05:45:40.835 eqi, eqj = i, j
2025-07-02 05:45:40.847 continue
2025-07-02 05:45:40.857 cruncher.set_seq1(ai)
2025-07-02 05:45:40.867 # computing similarity is expensive, so use the quick
2025-07-02 05:45:40.879 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:40.888 # compares by a factor of 3.
2025-07-02 05:45:40.895 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:40.909 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:40.919 # of the computation is cached by cruncher
2025-07-02 05:45:40.927 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:40.935 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:40.944 cruncher.ratio() > best_ratio:
2025-07-02 05:45:40.952 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:40.959 if best_ratio < cutoff:
2025-07-02 05:45:40.965 # no non-identical "pretty close" pair
2025-07-02 05:45:40.977 if eqi is None:
2025-07-02 05:45:40.986 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:40.994 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:41.003 return
2025-07-02 05:45:41.013 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:41.029 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:41.040 else:
2025-07-02 05:45:41.049 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:41.057 eqi = None
2025-07-02 05:45:41.063
2025-07-02 05:45:41.068 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:41.073 # identical
2025-07-02 05:45:41.078
2025-07-02 05:45:41.083 # pump out diffs from before the synch point
2025-07-02 05:45:41.091 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:41.099
2025-07-02 05:45:41.106 # do intraline marking on the synch pair
2025-07-02 05:45:41.113 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:41.120 if eqi is None:
2025-07-02 05:45:41.127 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:41.135 atags = btags = ""
2025-07-02 05:45:41.143 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:41.153 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:41.163 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:41.171 if tag == 'replace':
2025-07-02 05:45:41.178 atags += '^' * la
2025-07-02 05:45:41.185 btags += '^' * lb
2025-07-02 05:45:41.191 elif tag == 'delete':
2025-07-02 05:45:41.199 atags += '-' * la
2025-07-02 05:45:41.207 elif tag == 'insert':
2025-07-02 05:45:41.218 btags += '+' * lb
2025-07-02 05:45:41.229 elif tag == 'equal':
2025-07-02 05:45:41.241 atags += ' ' * la
2025-07-02 05:45:41.251 btags += ' ' * lb
2025-07-02 05:45:41.260 else:
2025-07-02 05:45:41.268 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:41.275 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:41.282 else:
2025-07-02 05:45:41.288 # the synch pair is identical
2025-07-02 05:45:41.294 yield '  ' + aelt
2025-07-02 05:45:41.300
2025-07-02 05:45:41.305 # pump out diffs from after the synch point
2025-07-02 05:45:41.314 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:41.321
2025-07-02 05:45:41.328 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:41.335 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:41.340
2025-07-02 05:45:41.346 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:41.354 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:41.360 alo = 16, ahi = 1101
2025-07-02 05:45:41.374 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:41.385 blo = 16, bhi = 1101
2025-07-02 05:45:41.394
2025-07-02 05:45:41.402 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:41.413 g = []
2025-07-02 05:45:41.424 if alo < ahi:
2025-07-02 05:45:41.431 if blo < bhi:
2025-07-02 05:45:41.438 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:41.444 else:
2025-07-02 05:45:41.451 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:41.459 elif blo < bhi:
2025-07-02 05:45:41.467 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:41.477
2025-07-02 05:45:41.489 >       yield from g
2025-07-02 05:45:41.497
2025-07-02 05:45:41.504 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:41.511 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:41.519
2025-07-02 05:45:41.529 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:41.538 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:41.548 alo = 16, ahi = 1101
2025-07-02 05:45:41.559 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:41.568 blo = 16, bhi = 1101
2025-07-02 05:45:41.577
2025-07-02 05:45:41.590 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:41.602 r"""
2025-07-02 05:45:41.612 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:41.619 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:41.624 synch point, and intraline difference marking is done on the
2025-07-02 05:45:41.629 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:41.633
2025-07-02 05:45:41.638 Example:
2025-07-02 05:45:41.643
2025-07-02 05:45:41.648 >>> d = Differ()
2025-07-02 05:45:41.654 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:41.664 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:41.674 >>> print(''.join(results), end="")
2025-07-02 05:45:41.683 - abcDefghiJkl
2025-07-02 05:45:41.699 + abcdefGhijkl
2025-07-02 05:45:41.723 """
2025-07-02 05:45:41.736
2025-07-02 05:45:41.749 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:41.761 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:41.771 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:41.779 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:41.787 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:41.793
2025-07-02 05:45:41.800 # search for the pair that matches best without being identical
2025-07-02 05:45:41.805 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:41.809 # on junk -- unless we have to)
2025-07-02 05:45:41.814 for j in range(blo, bhi):
2025-07-02 05:45:41.822 bj = b[j]
2025-07-02 05:45:41.832 cruncher.set_seq2(bj)
2025-07-02 05:45:41.840 for i in range(alo, ahi):
2025-07-02 05:45:41.847 ai = a[i]
2025-07-02 05:45:41.853 if ai == bj:
2025-07-02 05:45:41.859 if eqi is None:
2025-07-02 05:45:41.864 eqi, eqj = i, j
2025-07-02 05:45:41.870 continue
2025-07-02 05:45:41.878 cruncher.set_seq1(ai)
2025-07-02 05:45:41.885 # computing similarity is expensive, so use the quick
2025-07-02 05:45:41.897 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:41.909 # compares by a factor of 3.
2025-07-02 05:45:41.916 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:41.923 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:41.929 # of the computation is cached by cruncher
2025-07-02 05:45:41.939 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:41.946 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:41.954 cruncher.ratio() > best_ratio:
2025-07-02 05:45:41.966 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:41.975 if best_ratio < cutoff:
2025-07-02 05:45:41.983 # no non-identical "pretty close" pair
2025-07-02 05:45:41.996 if eqi is None:
2025-07-02 05:45:42.009 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:42.019 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:42.031 return
2025-07-02 05:45:42.044 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:42.055 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:42.063 else:
2025-07-02 05:45:42.072 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:42.086 eqi = None
2025-07-02 05:45:42.096
2025-07-02 05:45:42.105 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:42.112 # identical
2025-07-02 05:45:42.117
2025-07-02 05:45:42.124 # pump out diffs from before the synch point
2025-07-02 05:45:42.134 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:42.146
2025-07-02 05:45:42.157 # do intraline marking on the synch pair
2025-07-02 05:45:42.167 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:42.180 if eqi is None:
2025-07-02 05:45:42.189 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:42.197 atags = btags = ""
2025-07-02 05:45:42.205 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:42.218 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:42.227 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:42.234 if tag == 'replace':
2025-07-02 05:45:42.246 atags += '^' * la
2025-07-02 05:45:42.256 btags += '^' * lb
2025-07-02 05:45:42.264 elif tag == 'delete':
2025-07-02 05:45:42.270 atags += '-' * la
2025-07-02 05:45:42.276 elif tag == 'insert':
2025-07-02 05:45:42.282 btags += '+' * lb
2025-07-02 05:45:42.288 elif tag == 'equal':
2025-07-02 05:45:42.295 atags += ' ' * la
2025-07-02 05:45:42.303 btags += ' ' * lb
2025-07-02 05:45:42.314 else:
2025-07-02 05:45:42.323 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:42.330 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:42.336 else:
2025-07-02 05:45:42.342 # the synch pair is identical
2025-07-02 05:45:42.351 yield '  ' + aelt
2025-07-02 05:45:42.363
2025-07-02 05:45:42.374 # pump out diffs from after the synch point
2025-07-02 05:45:42.386 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:42.396
2025-07-02 05:45:42.403 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:42.411 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:42.420
2025-07-02 05:45:42.433 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:42.443 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:42.456 alo = 17, ahi = 1101
2025-07-02 05:45:42.466 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:42.477 blo = 17, bhi = 1101
2025-07-02 05:45:42.487
2025-07-02 05:45:42.494 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:42.502 g = []
2025-07-02 05:45:42.509 if alo < ahi:
2025-07-02 05:45:42.516 if blo < bhi:
2025-07-02 05:45:42.523 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:42.529 else:
2025-07-02 05:45:42.541 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:42.548 elif blo < bhi:
2025-07-02 05:45:42.555 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:42.561
2025-07-02 05:45:42.569 >       yield from g
2025-07-02 05:45:42.578
2025-07-02 05:45:42.585 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:42.598 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:42.611
2025-07-02 05:45:42.623 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:42.636 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:42.644 alo = 17, ahi = 1101
2025-07-02 05:45:42.653 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:42.658 blo = 17, bhi = 1101
2025-07-02 05:45:42.668
2025-07-02 05:45:42.677 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:42.686 r"""
2025-07-02 05:45:42.699 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:42.709 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:42.717 synch point, and intraline difference marking is done on the
2025-07-02 05:45:42.724 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:42.731
2025-07-02 05:45:42.737 Example:
2025-07-02 05:45:42.744
2025-07-02 05:45:42.750 >>> d = Differ()
2025-07-02 05:45:42.756 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:42.762 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:42.767 >>> print(''.join(results), end="")
2025-07-02 05:45:42.774 - abcDefghiJkl
2025-07-02 05:45:42.784 + abcdefGhijkl
2025-07-02 05:45:42.793 """
2025-07-02 05:45:42.798
2025-07-02 05:45:42.802 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:42.807 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:42.814 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:42.821 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:42.827 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:42.833
2025-07-02 05:45:42.838 # search for the pair that matches best without being identical
2025-07-02 05:45:42.845 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:42.858 # on junk -- unless we have to)
2025-07-02 05:45:42.867 for j in range(blo, bhi):
2025-07-02 05:45:42.876 bj = b[j]
2025-07-02 05:45:42.884 cruncher.set_seq2(bj)
2025-07-02 05:45:42.890 for i in range(alo, ahi):
2025-07-02 05:45:42.897 ai = a[i]
2025-07-02 05:45:42.910 if ai == bj:
2025-07-02 05:45:42.916 if eqi is None:
2025-07-02 05:45:42.922 eqi, eqj = i, j
2025-07-02 05:45:42.927 continue
2025-07-02 05:45:42.933 cruncher.set_seq1(ai)
2025-07-02 05:45:42.945 # computing similarity is expensive, so use the quick
2025-07-02 05:45:42.958 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:42.967 # compares by a factor of 3.
2025-07-02 05:45:42.975 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:42.981 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:42.988 # of the computation is cached by cruncher
2025-07-02 05:45:42.994 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:42.999 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:43.006 cruncher.ratio() > best_ratio:
2025-07-02 05:45:43.012 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:43.018 if best_ratio < cutoff:
2025-07-02 05:45:43.024 # no non-identical "pretty close" pair
2025-07-02 05:45:43.031 if eqi is None:
2025-07-02 05:45:43.038 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:43.049 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:43.059 return
2025-07-02 05:45:43.066 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:43.073 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:43.078 else:
2025-07-02 05:45:43.084 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:43.090 eqi = None
2025-07-02 05:45:43.097
2025-07-02 05:45:43.103 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:43.110 # identical
2025-07-02 05:45:43.116
2025-07-02 05:45:43.124 # pump out diffs from before the synch point
2025-07-02 05:45:43.132 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:43.139
2025-07-02 05:45:43.147 # do intraline marking on the synch pair
2025-07-02 05:45:43.154 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:43.165 if eqi is None:
2025-07-02 05:45:43.175 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:43.182 atags = btags = ""
2025-07-02 05:45:43.189 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:43.203 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:43.214 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:43.220 if tag == 'replace':
2025-07-02 05:45:43.225 atags += '^' * la
2025-07-02 05:45:43.230 btags += '^' * lb
2025-07-02 05:45:43.235 elif tag == 'delete':
2025-07-02 05:45:43.240 atags += '-' * la
2025-07-02 05:45:43.246 elif tag == 'insert':
2025-07-02 05:45:43.252 btags += '+' * lb
2025-07-02 05:45:43.258 elif tag == 'equal':
2025-07-02 05:45:43.264 atags += ' ' * la
2025-07-02 05:45:43.271 btags += ' ' * lb
2025-07-02 05:45:43.279 else:
2025-07-02 05:45:43.287 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:43.295 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:43.303 else:
2025-07-02 05:45:43.314 # the synch pair is identical
2025-07-02 05:45:43.325 yield '  ' + aelt
2025-07-02 05:45:43.332
2025-07-02 05:45:43.338 # pump out diffs from after the synch point
2025-07-02 05:45:43.345 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:43.353
2025-07-02 05:45:43.360 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:43.368 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:43.382
2025-07-02 05:45:43.393 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:43.401 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:43.408 alo = 18, ahi = 1101
2025-07-02 05:45:43.416 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:43.422 blo = 18, bhi = 1101
2025-07-02 05:45:43.427
2025-07-02 05:45:43.435 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:43.446 g = []
2025-07-02 05:45:43.455 if alo < ahi:
2025-07-02 05:45:43.465 if blo < bhi:
2025-07-02 05:45:43.476 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:43.484 else:
2025-07-02 05:45:43.494 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:43.508 elif blo < bhi:
2025-07-02 05:45:43.519 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:43.529
2025-07-02 05:45:43.540 >       yield from g
2025-07-02 05:45:43.550
2025-07-02 05:45:43.559 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:43.568 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:43.575
2025-07-02 05:45:43.585 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:43.595 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:43.608 alo = 18, ahi = 1101
2025-07-02 05:45:43.620 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:43.630 blo = 18, bhi = 1101
2025-07-02 05:45:43.642
2025-07-02 05:45:43.653 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:43.663 r"""
2025-07-02 05:45:43.673 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:43.680 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:43.688 synch point, and intraline difference marking is done on the
2025-07-02 05:45:43.694 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:43.700
2025-07-02 05:45:43.707 Example:
2025-07-02 05:45:43.712
2025-07-02 05:45:43.718 >>> d = Differ()
2025-07-02 05:45:43.725 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:43.738 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:43.749 >>> print(''.join(results), end="")
2025-07-02 05:45:43.761 - abcDefghiJkl
2025-07-02 05:45:43.780 + abcdefGhijkl
2025-07-02 05:45:43.795 """
2025-07-02 05:45:43.809
2025-07-02 05:45:43.821 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:43.831 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:43.839 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:43.846 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:43.856 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:43.867
2025-07-02 05:45:43.877 # search for the pair that matches best without being identical
2025-07-02 05:45:43.886 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:43.894 # on junk -- unless we have to)
2025-07-02 05:45:43.902 for j in range(blo, bhi):
2025-07-02 05:45:43.909 bj = b[j]
2025-07-02 05:45:43.914 cruncher.set_seq2(bj)
2025-07-02 05:45:43.920 for i in range(alo, ahi):
2025-07-02 05:45:43.925 ai = a[i]
2025-07-02 05:45:43.932 if ai == bj:
2025-07-02 05:45:43.938 if eqi is None:
2025-07-02 05:45:43.944 eqi, eqj = i, j
2025-07-02 05:45:43.953 continue
2025-07-02 05:45:43.967 cruncher.set_seq1(ai)
2025-07-02 05:45:43.977 # computing similarity is expensive, so use the quick
2025-07-02 05:45:43.985 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:43.991 # compares by a factor of 3.
2025-07-02 05:45:43.999 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:44.011 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:44.019 # of the computation is cached by cruncher
2025-07-02 05:45:44.029 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:44.042 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:44.053 cruncher.ratio() > best_ratio:
2025-07-02 05:45:44.064 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:44.075 if best_ratio < cutoff:
2025-07-02 05:45:44.086 # no non-identical "pretty close" pair
2025-07-02 05:45:44.095 if eqi is None:
2025-07-02 05:45:44.103 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:44.109 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:44.115 return
2025-07-02 05:45:44.123 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:44.133 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:44.141 else:
2025-07-02 05:45:44.148 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:44.155 eqi = None
2025-07-02 05:45:44.168
2025-07-02 05:45:44.180 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:44.190 # identical
2025-07-02 05:45:44.198
2025-07-02 05:45:44.205 # pump out diffs from before the synch point
2025-07-02 05:45:44.212 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:44.221
2025-07-02 05:45:44.234 # do intraline marking on the synch pair
2025-07-02 05:45:44.246 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:44.254 if eqi is None:
2025-07-02 05:45:44.266 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:44.277 atags = btags = ""
2025-07-02 05:45:44.289 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:44.304 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:44.312 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:44.319 if tag == 'replace':
2025-07-02 05:45:44.326 atags += '^' * la
2025-07-02 05:45:44.338 btags += '^' * lb
2025-07-02 05:45:44.349 elif tag == 'delete':
2025-07-02 05:45:44.357 atags += '-' * la
2025-07-02 05:45:44.369 elif tag == 'insert':
2025-07-02 05:45:44.379 btags += '+' * lb
2025-07-02 05:45:44.388 elif tag == 'equal':
2025-07-02 05:45:44.395 atags += ' ' * la
2025-07-02 05:45:44.402 btags += ' ' * lb
2025-07-02 05:45:44.414 else:
2025-07-02 05:45:44.425 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:44.434 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:44.446 else:
2025-07-02 05:45:44.460 # the synch pair is identical
2025-07-02 05:45:44.471 yield '  ' + aelt
2025-07-02 05:45:44.480
2025-07-02 05:45:44.489 # pump out diffs from after the synch point
2025-07-02 05:45:44.501 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:44.511
2025-07-02 05:45:44.523 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:44.533 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:44.540
2025-07-02 05:45:44.547 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:44.554 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:44.560 alo = 19, ahi = 1101
2025-07-02 05:45:44.568 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:44.576 blo = 19, bhi = 1101
2025-07-02 05:45:44.587
2025-07-02 05:45:44.598 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:44.609 g = []
2025-07-02 05:45:44.620 if alo < ahi:
2025-07-02 05:45:44.629 if blo < bhi:
2025-07-02 05:45:44.637 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:44.647 else:
2025-07-02 05:45:44.652 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:44.656 elif blo < bhi:
2025-07-02 05:45:44.661 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:44.665
2025-07-02 05:45:44.670 >       yield from g
2025-07-02 05:45:44.674
2025-07-02 05:45:44.679 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:44.683 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:44.688
2025-07-02 05:45:44.692 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:44.697 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:44.702 alo = 19, ahi = 1101
2025-07-02 05:45:44.707 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:44.711 blo = 19, bhi = 1101
2025-07-02 05:45:44.722
2025-07-02 05:45:44.730 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:44.739 r"""
2025-07-02 05:45:44.747 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:44.754 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:44.763 synch point, and intraline difference marking is done on the
2025-07-02 05:45:44.774 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:44.782
2025-07-02 05:45:44.788 Example:
2025-07-02 05:45:44.794
2025-07-02 05:45:44.801 >>> d = Differ()
2025-07-02 05:45:44.807 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:44.815 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:44.827 >>> print(''.join(results), end="")
2025-07-02 05:45:44.838 - abcDefghiJkl
2025-07-02 05:45:44.862 + abcdefGhijkl
2025-07-02 05:45:44.881 """
2025-07-02 05:45:44.891
2025-07-02 05:45:44.903 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:44.915 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:44.924 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:44.933 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:44.940 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:44.947
2025-07-02 05:45:44.954 # search for the pair that matches best without being identical
2025-07-02 05:45:44.963 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:44.972 # on junk -- unless we have to)
2025-07-02 05:45:44.979 for j in range(blo, bhi):
2025-07-02 05:45:44.985 bj = b[j]
2025-07-02 05:45:44.992 cruncher.set_seq2(bj)
2025-07-02 05:45:44.999 for i in range(alo, ahi):
2025-07-02 05:45:45.005 ai = a[i]
2025-07-02 05:45:45.012 if ai == bj:
2025-07-02 05:45:45.018 if eqi is None:
2025-07-02 05:45:45.030 eqi, eqj = i, j
2025-07-02 05:45:45.044 continue
2025-07-02 05:45:45.057 cruncher.set_seq1(ai)
2025-07-02 05:45:45.069 # computing similarity is expensive, so use the quick
2025-07-02 05:45:45.077 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:45.084 # compares by a factor of 3.
2025-07-02 05:45:45.090 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:45.097 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:45.104 # of the computation is cached by cruncher
2025-07-02 05:45:45.111 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:45.119 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:45.128 cruncher.ratio() > best_ratio:
2025-07-02 05:45:45.135 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:45.142 if best_ratio < cutoff:
2025-07-02 05:45:45.151 # no non-identical "pretty close" pair
2025-07-02 05:45:45.162 if eqi is None:
2025-07-02 05:45:45.170 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:45.178 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:45.185 return
2025-07-02 05:45:45.193 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:45.200 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:45.207 else:
2025-07-02 05:45:45.214 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:45.222 eqi = None
2025-07-02 05:45:45.234
2025-07-02 05:45:45.244 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:45.252 # identical
2025-07-02 05:45:45.259
2025-07-02 05:45:45.265 # pump out diffs from before the synch point
2025-07-02 05:45:45.279 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:45.288
2025-07-02 05:45:45.297 # do intraline marking on the synch pair
2025-07-02 05:45:45.305 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:45.317 if eqi is None:
2025-07-02 05:45:45.327 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:45.335 atags = btags = ""
2025-07-02 05:45:45.342 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:45.350 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:45.357 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:45.364 if tag == 'replace':
2025-07-02 05:45:45.372 atags += '^' * la
2025-07-02 05:45:45.380 btags += '^' * lb
2025-07-02 05:45:45.388 elif tag == 'delete':
2025-07-02 05:45:45.397 atags += '-' * la
2025-07-02 05:45:45.410 elif tag == 'insert':
2025-07-02 05:45:45.421 btags += '+' * lb
2025-07-02 05:45:45.430 elif tag == 'equal':
2025-07-02 05:45:45.438 atags += ' ' * la
2025-07-02 05:45:45.445 btags += ' ' * lb
2025-07-02 05:45:45.452 else:
2025-07-02 05:45:45.460 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:45.468 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:45.476 else:
2025-07-02 05:45:45.484 # the synch pair is identical
2025-07-02 05:45:45.491 yield '  ' + aelt
2025-07-02 05:45:45.499
2025-07-02 05:45:45.507 # pump out diffs from after the synch point
2025-07-02 05:45:45.515 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:45.526
2025-07-02 05:45:45.540 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:45.549 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:45.558
2025-07-02 05:45:45.571 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:45.581 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:45.588 alo = 20, ahi = 1101
2025-07-02 05:45:45.595 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:45.601 blo = 20, bhi = 1101
2025-07-02 05:45:45.606
2025-07-02 05:45:45.613 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:45.620 g = []
2025-07-02 05:45:45.628 if alo < ahi:
2025-07-02 05:45:45.636 if blo < bhi:
2025-07-02 05:45:45.643 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:45.651 else:
2025-07-02 05:45:45.659 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:45.667 elif blo < bhi:
2025-07-02 05:45:45.679 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:45.689
2025-07-02 05:45:45.697 >       yield from g
2025-07-02 05:45:45.704
2025-07-02 05:45:45.710 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:45.717 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:45.725
2025-07-02 05:45:45.736 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:45.748 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:45.760 alo = 20, ahi = 1101
2025-07-02 05:45:45.773 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:45.780 blo = 20, bhi = 1101
2025-07-02 05:45:45.787
2025-07-02 05:45:45.795 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:45.804 r"""
2025-07-02 05:45:45.815 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:45.827 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:45.838 synch point, and intraline difference marking is done on the
2025-07-02 05:45:45.846 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:45.853
2025-07-02 05:45:45.859 Example:
2025-07-02 05:45:45.866
2025-07-02 05:45:45.879 >>> d = Differ()
2025-07-02 05:45:45.891 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:45.903 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:45.914 >>> print(''.join(results), end="")
2025-07-02 05:45:45.921 - abcDefghiJkl
2025-07-02 05:45:45.933 + abcdefGhijkl
2025-07-02 05:45:45.944 """
2025-07-02 05:45:45.949
2025-07-02 05:45:45.960 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:45.970 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:45.982 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:45.990 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:46.005 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:46.018
2025-07-02 05:45:46.031 # search for the pair that matches best without being identical
2025-07-02 05:45:46.043 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:46.049 # on junk -- unless we have to)
2025-07-02 05:45:46.056 for j in range(blo, bhi):
2025-07-02 05:45:46.063 bj = b[j]
2025-07-02 05:45:46.070 cruncher.set_seq2(bj)
2025-07-02 05:45:46.077 for i in range(alo, ahi):
2025-07-02 05:45:46.084 ai = a[i]
2025-07-02 05:45:46.091 if ai == bj:
2025-07-02 05:45:46.099 if eqi is None:
2025-07-02 05:45:46.110 eqi, eqj = i, j
2025-07-02 05:45:46.118 continue
2025-07-02 05:45:46.125 cruncher.set_seq1(ai)
2025-07-02 05:45:46.138 # computing similarity is expensive, so use the quick
2025-07-02 05:45:46.148 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:46.155 # compares by a factor of 3.
2025-07-02 05:45:46.164 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:46.175 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:46.184 # of the computation is cached by cruncher
2025-07-02 05:45:46.192 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:46.199 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:46.207 cruncher.ratio() > best_ratio:
2025-07-02 05:45:46.218 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:46.228 if best_ratio < cutoff:
2025-07-02 05:45:46.237 # no non-identical "pretty close" pair
2025-07-02 05:45:46.248 if eqi is None:
2025-07-02 05:45:46.262 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:46.272 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:46.280 return
2025-07-02 05:45:46.289 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:46.295 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:46.309 else:
2025-07-02 05:45:46.319 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:46.327 eqi = None
2025-07-02 05:45:46.335
2025-07-02 05:45:46.342 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:46.349 # identical
2025-07-02 05:45:46.355
2025-07-02 05:45:46.364 # pump out diffs from before the synch point
2025-07-02 05:45:46.376 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:46.386
2025-07-02 05:45:46.395 # do intraline marking on the synch pair
2025-07-02 05:45:46.403 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:46.411 if eqi is None:
2025-07-02 05:45:46.418 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:46.431 atags = btags = ""
2025-07-02 05:45:46.444 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:46.455 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:46.464 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:46.470 if tag == 'replace':
2025-07-02 05:45:46.475 atags += '^' * la
2025-07-02 05:45:46.480 btags += '^' * lb
2025-07-02 05:45:46.485 elif tag == 'delete':
2025-07-02 05:45:46.490 atags += '-' * la
2025-07-02 05:45:46.495 elif tag == 'insert':
2025-07-02 05:45:46.500 btags += '+' * lb
2025-07-02 05:45:46.505 elif tag == 'equal':
2025-07-02 05:45:46.510 atags += ' ' * la
2025-07-02 05:45:46.515 btags += ' ' * lb
2025-07-02 05:45:46.522 else:
2025-07-02 05:45:46.528 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:46.534 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:46.540 else:
2025-07-02 05:45:46.546 # the synch pair is identical
2025-07-02 05:45:46.551 yield '  ' + aelt
2025-07-02 05:45:46.562
2025-07-02 05:45:46.574 # pump out diffs from after the synch point
2025-07-02 05:45:46.584 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:46.591
2025-07-02 05:45:46.598 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:46.603 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:46.612
2025-07-02 05:45:46.623 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:46.634 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:46.648 alo = 21, ahi = 1101
2025-07-02 05:45:46.658 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:46.668 blo = 21, bhi = 1101
2025-07-02 05:45:46.676
2025-07-02 05:45:46.683 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:46.690 g = []
2025-07-02 05:45:46.697 if alo < ahi:
2025-07-02 05:45:46.702 if blo < bhi:
2025-07-02 05:45:46.708 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:46.718 else:
2025-07-02 05:45:46.729 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:46.740 elif blo < bhi:
2025-07-02 05:45:46.750 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:46.757
2025-07-02 05:45:46.765 >       yield from g
2025-07-02 05:45:46.771
2025-07-02 05:45:46.777 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:46.783 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:46.790
2025-07-02 05:45:46.796 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:46.804 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:46.811 alo = 21, ahi = 1101
2025-07-02 05:45:46.824 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:46.837 blo = 21, bhi = 1101
2025-07-02 05:45:46.850
2025-07-02 05:45:46.862 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:46.872 r"""
2025-07-02 05:45:46.883 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:46.894 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:46.904 synch point, and intraline difference marking is done on the
2025-07-02 05:45:46.912 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:46.920
2025-07-02 05:45:46.926 Example:
2025-07-02 05:45:46.933
2025-07-02 05:45:46.942 >>> d = Differ()
2025-07-02 05:45:46.955 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:46.966 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:46.975 >>> print(''.join(results), end="")
2025-07-02 05:45:46.987 - abcDefghiJkl
2025-07-02 05:45:47.012 + abcdefGhijkl
2025-07-02 05:45:47.034 """
2025-07-02 05:45:47.045
2025-07-02 05:45:47.054 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:47.067 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:47.077 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:47.091 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:47.100 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:47.106
2025-07-02 05:45:47.113 # search for the pair that matches best without being identical
2025-07-02 05:45:47.120 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:47.125 # on junk -- unless we have to)
2025-07-02 05:45:47.131 for j in range(blo, bhi):
2025-07-02 05:45:47.136 bj = b[j]
2025-07-02 05:45:47.147 cruncher.set_seq2(bj)
2025-07-02 05:45:47.154 for i in range(alo, ahi):
2025-07-02 05:45:47.160 ai = a[i]
2025-07-02 05:45:47.165 if ai == bj:
2025-07-02 05:45:47.171 if eqi is None:
2025-07-02 05:45:47.178 eqi, eqj = i, j
2025-07-02 05:45:47.184 continue
2025-07-02 05:45:47.190 cruncher.set_seq1(ai)
2025-07-02 05:45:47.197 # computing similarity is expensive, so use the quick
2025-07-02 05:45:47.203 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:47.209 # compares by a factor of 3.
2025-07-02 05:45:47.217 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:47.223 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:47.230 # of the computation is cached by cruncher
2025-07-02 05:45:47.237 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:47.244 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:47.250 cruncher.ratio() > best_ratio:
2025-07-02 05:45:47.257 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:47.263 if best_ratio < cutoff:
2025-07-02 05:45:47.268 # no non-identical "pretty close" pair
2025-07-02 05:45:47.279 if eqi is None:
2025-07-02 05:45:47.291 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:47.300 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:47.307 return
2025-07-02 05:45:47.313 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:47.320 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:47.327 else:
2025-07-02 05:45:47.338 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:47.346 eqi = None
2025-07-02 05:45:47.356
2025-07-02 05:45:47.365 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:47.372 # identical
2025-07-02 05:45:47.379
2025-07-02 05:45:47.387 # pump out diffs from before the synch point
2025-07-02 05:45:47.397 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:47.405
2025-07-02 05:45:47.412 # do intraline marking on the synch pair
2025-07-02 05:45:47.419 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:47.427 if eqi is None:
2025-07-02 05:45:47.437 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:47.449 atags = btags = ""
2025-07-02 05:45:47.461 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:47.469 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:47.476 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:47.483 if tag == 'replace':
2025-07-02 05:45:47.490 atags += '^' * la
2025-07-02 05:45:47.498 btags += '^' * lb
2025-07-02 05:45:47.505 elif tag == 'delete':
2025-07-02 05:45:47.512 atags += '-' * la
2025-07-02 05:45:47.518 elif tag == 'insert':
2025-07-02 05:45:47.529 btags += '+' * lb
2025-07-02 05:45:47.539 elif tag == 'equal':
2025-07-02 05:45:47.547 atags += ' ' * la
2025-07-02 05:45:47.554 btags += ' ' * lb
2025-07-02 05:45:47.560 else:
2025-07-02 05:45:47.565 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:47.570 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:47.574 else:
2025-07-02 05:45:47.584 # the synch pair is identical
2025-07-02 05:45:47.597 yield '  ' + aelt
2025-07-02 05:45:47.605
2025-07-02 05:45:47.612 # pump out diffs from after the synch point
2025-07-02 05:45:47.620 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:47.626
2025-07-02 05:45:47.633 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:47.642 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:47.653
2025-07-02 05:45:47.666 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:47.673 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:47.679 alo = 22, ahi = 1101
2025-07-02 05:45:47.687 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:47.694 blo = 22, bhi = 1101
2025-07-02 05:45:47.708
2025-07-02 05:45:47.717 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:47.724 g = []
2025-07-02 05:45:47.731 if alo < ahi:
2025-07-02 05:45:47.741 if blo < bhi:
2025-07-02 05:45:47.751 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:47.760 else:
2025-07-02 05:45:47.767 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:47.781 elif blo < bhi:
2025-07-02 05:45:47.791 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:47.800
2025-07-02 05:45:47.807 >       yield from g
2025-07-02 05:45:47.813
2025-07-02 05:45:47.828 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:47.839 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:47.851
2025-07-02 05:45:47.859 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:47.866 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:47.872 alo = 22, ahi = 1101
2025-07-02 05:45:47.880 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:47.886 blo = 22, bhi = 1101
2025-07-02 05:45:47.894
2025-07-02 05:45:47.905 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:47.919 r"""
2025-07-02 05:45:47.927 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:47.935 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:47.941 synch point, and intraline difference marking is done on the
2025-07-02 05:45:47.948 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:47.955
2025-07-02 05:45:47.963 Example:
2025-07-02 05:45:47.975
2025-07-02 05:45:47.986 >>> d = Differ()
2025-07-02 05:45:47.996 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:48.004 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:48.011 >>> print(''.join(results), end="")
2025-07-02 05:45:48.016 - abcDefghiJkl
2025-07-02 05:45:48.027 + abcdefGhijkl
2025-07-02 05:45:48.040 """
2025-07-02 05:45:48.047
2025-07-02 05:45:48.055 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:48.062 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:48.072 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:48.084 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:48.094 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:48.101
2025-07-02 05:45:48.113 # search for the pair that matches best without being identical
2025-07-02 05:45:48.121 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:48.128 # on junk -- unless we have to)
2025-07-02 05:45:48.136 for j in range(blo, bhi):
2025-07-02 05:45:48.145 bj = b[j]
2025-07-02 05:45:48.153 cruncher.set_seq2(bj)
2025-07-02 05:45:48.161 for i in range(alo, ahi):
2025-07-02 05:45:48.167 ai = a[i]
2025-07-02 05:45:48.175 if ai == bj:
2025-07-02 05:45:48.186 if eqi is None:
2025-07-02 05:45:48.192 eqi, eqj = i, j
2025-07-02 05:45:48.198 continue
2025-07-02 05:45:48.204 cruncher.set_seq1(ai)
2025-07-02 05:45:48.211 # computing similarity is expensive, so use the quick
2025-07-02 05:45:48.218 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:48.226 # compares by a factor of 3.
2025-07-02 05:45:48.235 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:48.246 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:48.255 # of the computation is cached by cruncher
2025-07-02 05:45:48.266 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:48.274 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:48.282 cruncher.ratio() > best_ratio:
2025-07-02 05:45:48.291 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:48.300 if best_ratio < cutoff:
2025-07-02 05:45:48.309 # no non-identical "pretty close" pair
2025-07-02 05:45:48.321 if eqi is None:
2025-07-02 05:45:48.332 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:48.341 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:48.349 return
2025-07-02 05:45:48.355 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:48.362 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:48.367 else:
2025-07-02 05:45:48.373 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:48.379 eqi = None
2025-07-02 05:45:48.384
2025-07-02 05:45:48.390 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:48.402 # identical
2025-07-02 05:45:48.413
2025-07-02 05:45:48.421 # pump out diffs from before the synch point
2025-07-02 05:45:48.427 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:48.434
2025-07-02 05:45:48.446 # do intraline marking on the synch pair
2025-07-02 05:45:48.456 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:48.462 if eqi is None:
2025-07-02 05:45:48.468 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:48.472 atags = btags = ""
2025-07-02 05:45:48.477 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:48.482 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:48.486 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:48.491 if tag == 'replace':
2025-07-02 05:45:48.495 atags += '^' * la
2025-07-02 05:45:48.500 btags += '^' * lb
2025-07-02 05:45:48.505 elif tag == 'delete':
2025-07-02 05:45:48.510 atags += '-' * la
2025-07-02 05:45:48.515 elif tag == 'insert':
2025-07-02 05:45:48.520 btags += '+' * lb
2025-07-02 05:45:48.526 elif tag == 'equal':
2025-07-02 05:45:48.532 atags += ' ' * la
2025-07-02 05:45:48.538 btags += ' ' * lb
2025-07-02 05:45:48.544 else:
2025-07-02 05:45:48.550 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:48.558 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:48.570 else:
2025-07-02 05:45:48.582 # the synch pair is identical
2025-07-02 05:45:48.593 yield '  ' + aelt
2025-07-02 05:45:48.601
2025-07-02 05:45:48.609 # pump out diffs from after the synch point
2025-07-02 05:45:48.616 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:48.624
2025-07-02 05:45:48.631 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:48.639 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:48.646
2025-07-02 05:45:48.655 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:48.667 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:48.676 alo = 23, ahi = 1101
2025-07-02 05:45:48.683 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:48.689 blo = 23, bhi = 1101
2025-07-02 05:45:48.694
2025-07-02 05:45:48.700 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:48.705 g = []
2025-07-02 05:45:48.711 if alo < ahi:
2025-07-02 05:45:48.717 if blo < bhi:
2025-07-02 05:45:48.723 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:48.731 else:
2025-07-02 05:45:48.743 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:48.752 elif blo < bhi:
2025-07-02 05:45:48.759 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:48.767
2025-07-02 05:45:48.776 >       yield from g
2025-07-02 05:45:48.784
2025-07-02 05:45:48.794 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:48.807 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:48.815
2025-07-02 05:45:48.825 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:48.834 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:48.845 alo = 23, ahi = 1101
2025-07-02 05:45:48.858 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:48.870 blo = 23, bhi = 1101
2025-07-02 05:45:48.880
2025-07-02 05:45:48.891 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:48.903 r"""
2025-07-02 05:45:48.916 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:48.925 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:48.934 synch point, and intraline difference marking is done on the
2025-07-02 05:45:48.945 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:48.957
2025-07-02 05:45:48.966 Example:
2025-07-02 05:45:48.974
2025-07-02 05:45:48.982 >>> d = Differ()
2025-07-02 05:45:48.989 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:48.996 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:49.003 >>> print(''.join(results), end="")
2025-07-02 05:45:49.010 - abcDefghiJkl
2025-07-02 05:45:49.023 + abcdefGhijkl
2025-07-02 05:45:49.047 """
2025-07-02 05:45:49.058
2025-07-02 05:45:49.072 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:49.085 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:49.099 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:49.112 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:49.122 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:49.136
2025-07-02 05:45:49.146 # search for the pair that matches best without being identical
2025-07-02 05:45:49.154 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:49.162 # on junk -- unless we have to)
2025-07-02 05:45:49.169 for j in range(blo, bhi):
2025-07-02 05:45:49.175 bj = b[j]
2025-07-02 05:45:49.182 cruncher.set_seq2(bj)
2025-07-02 05:45:49.189 for i in range(alo, ahi):
2025-07-02 05:45:49.195 ai = a[i]
2025-07-02 05:45:49.202 if ai == bj:
2025-07-02 05:45:49.212 if eqi is None:
2025-07-02 05:45:49.224 eqi, eqj = i, j
2025-07-02 05:45:49.235 continue
2025-07-02 05:45:49.248 cruncher.set_seq1(ai)
2025-07-02 05:45:49.259 # computing similarity is expensive, so use the quick
2025-07-02 05:45:49.268 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:49.275 # compares by a factor of 3.
2025-07-02 05:45:49.282 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:49.291 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:49.302 # of the computation is cached by cruncher
2025-07-02 05:45:49.308 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:49.322 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:49.335 cruncher.ratio() > best_ratio:
2025-07-02 05:45:49.348 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:49.360 if best_ratio < cutoff:
2025-07-02 05:45:49.369 # no non-identical "pretty close" pair
2025-07-02 05:45:49.380 if eqi is None:
2025-07-02 05:45:49.392 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:49.401 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:49.408 return
2025-07-02 05:45:49.416 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:49.422 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:49.428 else:
2025-07-02 05:45:49.436 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:49.443 eqi = None
2025-07-02 05:45:49.455
2025-07-02 05:45:49.466 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:49.474 # identical
2025-07-02 05:45:49.480
2025-07-02 05:45:49.487 # pump out diffs from before the synch point
2025-07-02 05:45:49.495 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:49.507
2025-07-02 05:45:49.516 # do intraline marking on the synch pair
2025-07-02 05:45:49.524 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:49.531 if eqi is None:
2025-07-02 05:45:49.539 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:49.550 atags = btags = ""
2025-07-02 05:45:49.560 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:49.569 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:49.581 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:49.591 if tag == 'replace':
2025-07-02 05:45:49.599 atags += '^' * la
2025-07-02 05:45:49.605 btags += '^' * lb
2025-07-02 05:45:49.612 elif tag == 'delete':
2025-07-02 05:45:49.619 atags += '-' * la
2025-07-02 05:45:49.625 elif tag == 'insert':
2025-07-02 05:45:49.632 btags += '+' * lb
2025-07-02 05:45:49.639 elif tag == 'equal':
2025-07-02 05:45:49.647 atags += ' ' * la
2025-07-02 05:45:49.655 btags += ' ' * lb
2025-07-02 05:45:49.667 else:
2025-07-02 05:45:49.675 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:49.682 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:49.689 else:
2025-07-02 05:45:49.696 # the synch pair is identical
2025-07-02 05:45:49.704 yield '  ' + aelt
2025-07-02 05:45:49.711
2025-07-02 05:45:49.717 # pump out diffs from after the synch point
2025-07-02 05:45:49.722 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:49.727
2025-07-02 05:45:49.732 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:49.737 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:49.742
2025-07-02 05:45:49.748 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:49.755 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:49.767 alo = 26, ahi = 1101
2025-07-02 05:45:49.781 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:49.794 blo = 26, bhi = 1101
2025-07-02 05:45:49.808
2025-07-02 05:45:49.820 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:49.832 g = []
2025-07-02 05:45:49.841 if alo < ahi:
2025-07-02 05:45:49.848 if blo < bhi:
2025-07-02 05:45:49.854 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:49.865 else:
2025-07-02 05:45:49.875 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:49.884 elif blo < bhi:
2025-07-02 05:45:49.892 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:49.899
2025-07-02 05:45:49.905 >       yield from g
2025-07-02 05:45:49.911
2025-07-02 05:45:49.918 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:49.924 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:49.930
2025-07-02 05:45:49.942 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:49.952 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:49.960 alo = 26, ahi = 1101
2025-07-02 05:45:49.968 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:49.982 blo = 26, bhi = 1101
2025-07-02 05:45:49.995
2025-07-02 05:45:50.009 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:50.019 r"""
2025-07-02 05:45:50.027 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:50.040 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:50.051 synch point, and intraline difference marking is done on the
2025-07-02 05:45:50.060 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:50.067
2025-07-02 05:45:50.073 Example:
2025-07-02 05:45:50.079
2025-07-02 05:45:50.085 >>> d = Differ()
2025-07-02 05:45:50.092 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:50.099 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:50.109 >>> print(''.join(results), end="")
2025-07-02 05:45:50.123 - abcDefghiJkl
2025-07-02 05:45:50.148 + abcdefGhijkl
2025-07-02 05:45:50.172 """
2025-07-02 05:45:50.185
2025-07-02 05:45:50.194 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:50.203 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:50.209 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:50.216 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:50.222 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:50.231
2025-07-02 05:45:50.240 # search for the pair that matches best without being identical
2025-07-02 05:45:50.248 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:50.255 # on junk -- unless we have to)
2025-07-02 05:45:50.263 for j in range(blo, bhi):
2025-07-02 05:45:50.276 bj = b[j]
2025-07-02 05:45:50.285 cruncher.set_seq2(bj)
2025-07-02 05:45:50.292 for i in range(alo, ahi):
2025-07-02 05:45:50.299 ai = a[i]
2025-07-02 05:45:50.308 if ai == bj:
2025-07-02 05:45:50.320 if eqi is None:
2025-07-02 05:45:50.329 eqi, eqj = i, j
2025-07-02 05:45:50.336 continue
2025-07-02 05:45:50.344 cruncher.set_seq1(ai)
2025-07-02 05:45:50.358 # computing similarity is expensive, so use the quick
2025-07-02 05:45:50.370 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:50.383 # compares by a factor of 3.
2025-07-02 05:45:50.394 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:50.403 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:50.411 # of the computation is cached by cruncher
2025-07-02 05:45:50.419 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:50.430 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:50.438 cruncher.ratio() > best_ratio:
2025-07-02 05:45:50.447 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:50.453 if best_ratio < cutoff:
2025-07-02 05:45:50.468 # no non-identical "pretty close" pair
2025-07-02 05:45:50.479 if eqi is None:
2025-07-02 05:45:50.494 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:50.504 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:50.512 return
2025-07-02 05:45:50.521 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:50.533 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:50.545 else:
2025-07-02 05:45:50.554 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:50.563 eqi = None
2025-07-02 05:45:50.571
2025-07-02 05:45:50.580 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:50.587 # identical
2025-07-02 05:45:50.594
2025-07-02 05:45:50.601 # pump out diffs from before the synch point
2025-07-02 05:45:50.607 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:50.615
2025-07-02 05:45:50.626 # do intraline marking on the synch pair
2025-07-02 05:45:50.638 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:50.649 if eqi is None:
2025-07-02 05:45:50.659 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:50.671 atags = btags = ""
2025-07-02 05:45:50.681 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:50.690 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:50.699 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:50.706 if tag == 'replace':
2025-07-02 05:45:50.715 atags += '^' * la
2025-07-02 05:45:50.728 btags += '^' * lb
2025-07-02 05:45:50.740 elif tag == 'delete':
2025-07-02 05:45:50.754 atags += '-' * la
2025-07-02 05:45:50.765 elif tag == 'insert':
2025-07-02 05:45:50.776 btags += '+' * lb
2025-07-02 05:45:50.788 elif tag == 'equal':
2025-07-02 05:45:50.800 atags += ' ' * la
2025-07-02 05:45:50.813 btags += ' ' * lb
2025-07-02 05:45:50.823 else:
2025-07-02 05:45:50.832 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:50.840 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:50.846 else:
2025-07-02 05:45:50.858 # the synch pair is identical
2025-07-02 05:45:50.868 yield '  ' + aelt
2025-07-02 05:45:50.876
2025-07-02 05:45:50.883 # pump out diffs from after the synch point
2025-07-02 05:45:50.891 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:50.897
2025-07-02 05:45:50.903 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:50.910 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:50.921
2025-07-02 05:45:50.932 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:50.946 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:50.959 alo = 27, ahi = 1101
2025-07-02 05:45:50.973 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:50.985 blo = 27, bhi = 1101
2025-07-02 05:45:50.998
2025-07-02 05:45:51.011 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:51.025 g = []
2025-07-02 05:45:51.038 if alo < ahi:
2025-07-02 05:45:51.047 if blo < bhi:
2025-07-02 05:45:51.060 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:51.072 else:
2025-07-02 05:45:51.081 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:51.092 elif blo < bhi:
2025-07-02 05:45:51.101 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:51.109
2025-07-02 05:45:51.117 >       yield from g
2025-07-02 05:45:51.124
2025-07-02 05:45:51.131 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:51.138 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:51.148
2025-07-02 05:45:51.161 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:51.173 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:51.183 alo = 27, ahi = 1101
2025-07-02 05:45:51.192 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:51.198 blo = 27, bhi = 1101
2025-07-02 05:45:51.205
2025-07-02 05:45:51.211 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:51.221 r"""
2025-07-02 05:45:51.229 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:51.236 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:51.243 synch point, and intraline difference marking is done on the
2025-07-02 05:45:51.250 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:51.255
2025-07-02 05:45:51.263 Example:
2025-07-02 05:45:51.269
2025-07-02 05:45:51.275 >>> d = Differ()
2025-07-02 05:45:51.282 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:51.292 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:51.298 >>> print(''.join(results), end="")
2025-07-02 05:45:51.304 - abcDefghiJkl
2025-07-02 05:45:51.315 + abcdefGhijkl
2025-07-02 05:45:51.324 """
2025-07-02 05:45:51.329
2025-07-02 05:45:51.337 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:51.346 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:51.359 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:51.373 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:51.387 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:51.398
2025-07-02 05:45:51.405 # search for the pair that matches best without being identical
2025-07-02 05:45:51.410 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:51.415 # on junk -- unless we have to)
2025-07-02 05:45:51.421 for j in range(blo, bhi):
2025-07-02 05:45:51.427 bj = b[j]
2025-07-02 05:45:51.433 cruncher.set_seq2(bj)
2025-07-02 05:45:51.438 for i in range(alo, ahi):
2025-07-02 05:45:51.449 ai = a[i]
2025-07-02 05:45:51.459 if ai == bj:
2025-07-02 05:45:51.467 if eqi is None:
2025-07-02 05:45:51.475 eqi, eqj = i, j
2025-07-02 05:45:51.484 continue
2025-07-02 05:45:51.492 cruncher.set_seq1(ai)
2025-07-02 05:45:51.499 # computing similarity is expensive, so use the quick
2025-07-02 05:45:51.507 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:51.514 # compares by a factor of 3.
2025-07-02 05:45:51.521 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:51.528 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:51.535 # of the computation is cached by cruncher
2025-07-02 05:45:51.543 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:51.550 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:51.559 cruncher.ratio() > best_ratio:
2025-07-02 05:45:51.567 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:51.575 if best_ratio < cutoff:
2025-07-02 05:45:51.589 # no non-identical "pretty close" pair
2025-07-02 05:45:51.601 if eqi is None:
2025-07-02 05:45:51.613 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:51.625 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:51.635 return
2025-07-02 05:45:51.647 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:51.657 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:51.665 else:
2025-07-02 05:45:51.672 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:51.678 eqi = None
2025-07-02 05:45:51.686
2025-07-02 05:45:51.695 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:51.703 # identical
2025-07-02 05:45:51.709
2025-07-02 05:45:51.715 # pump out diffs from before the synch point
2025-07-02 05:45:51.721 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:51.725
2025-07-02 05:45:51.732 # do intraline marking on the synch pair
2025-07-02 05:45:51.740 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:51.747 if eqi is None:
2025-07-02 05:45:51.754 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:51.765 atags = btags = ""
2025-07-02 05:45:51.777 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:51.786 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:51.795 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:51.807 if tag == 'replace':
2025-07-02 05:45:51.815 atags += '^' * la
2025-07-02 05:45:51.824 btags += '^' * lb
2025-07-02 05:45:51.830 elif tag == 'delete':
2025-07-02 05:45:51.842 atags += '-' * la
2025-07-02 05:45:51.853 elif tag == 'insert':
2025-07-02 05:45:51.861 btags += '+' * lb
2025-07-02 05:45:51.867 elif tag == 'equal':
2025-07-02 05:45:51.873 atags += ' ' * la
2025-07-02 05:45:51.880 btags += ' ' * lb
2025-07-02 05:45:51.886 else:
2025-07-02 05:45:51.900 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:51.914 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:51.925 else:
2025-07-02 05:45:51.935 # the synch pair is identical
2025-07-02 05:45:51.943 yield '  ' + aelt
2025-07-02 05:45:51.959
2025-07-02 05:45:51.972 # pump out diffs from after the synch point
2025-07-02 05:45:51.984 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:51.994
2025-07-02 05:45:52.005 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:52.012 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:52.019
2025-07-02 05:45:52.027 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:52.036 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:52.043 alo = 28, ahi = 1101
2025-07-02 05:45:52.049 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:52.055 blo = 28, bhi = 1101
2025-07-02 05:45:52.063
2025-07-02 05:45:52.074 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:52.083 g = []
2025-07-02 05:45:52.095 if alo < ahi:
2025-07-02 05:45:52.108 if blo < bhi:
2025-07-02 05:45:52.118 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:52.126 else:
2025-07-02 05:45:52.134 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:52.145 elif blo < bhi:
2025-07-02 05:45:52.155 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:52.163
2025-07-02 05:45:52.171 >       yield from g
2025-07-02 05:45:52.179
2025-07-02 05:45:52.191 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:52.199 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:52.215
2025-07-02 05:45:52.225 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:52.234 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:52.248 alo = 28, ahi = 1101
2025-07-02 05:45:52.263 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:52.271 blo = 28, bhi = 1101
2025-07-02 05:45:52.278
2025-07-02 05:45:52.286 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:52.298 r"""
2025-07-02 05:45:52.306 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:52.314 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:52.328 synch point, and intraline difference marking is done on the
2025-07-02 05:45:52.340 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:52.352
2025-07-02 05:45:52.362 Example:
2025-07-02 05:45:52.369
2025-07-02 05:45:52.376 >>> d = Differ()
2025-07-02 05:45:52.390 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:52.398 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:52.409 >>> print(''.join(results), end="")
2025-07-02 05:45:52.420 - abcDefghiJkl
2025-07-02 05:45:52.436 + abcdefGhijkl
2025-07-02 05:45:52.463 """
2025-07-02 05:45:52.472
2025-07-02 05:45:52.479 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:52.486 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:52.497 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:52.508 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:52.517 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:52.526
2025-07-02 05:45:52.538 # search for the pair that matches best without being identical
2025-07-02 05:45:52.548 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:52.555 # on junk -- unless we have to)
2025-07-02 05:45:52.562 for j in range(blo, bhi):
2025-07-02 05:45:52.567 bj = b[j]
2025-07-02 05:45:52.572 cruncher.set_seq2(bj)
2025-07-02 05:45:52.577 for i in range(alo, ahi):
2025-07-02 05:45:52.582 ai = a[i]
2025-07-02 05:45:52.589 if ai == bj:
2025-07-02 05:45:52.595 if eqi is None:
2025-07-02 05:45:52.602 eqi, eqj = i, j
2025-07-02 05:45:52.609 continue
2025-07-02 05:45:52.615 cruncher.set_seq1(ai)
2025-07-02 05:45:52.622 # computing similarity is expensive, so use the quick
2025-07-02 05:45:52.631 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:52.644 # compares by a factor of 3.
2025-07-02 05:45:52.655 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:52.663 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:52.676 # of the computation is cached by cruncher
2025-07-02 05:45:52.689 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:52.698 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:52.710 cruncher.ratio() > best_ratio:
2025-07-02 05:45:52.720 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:52.730 if best_ratio < cutoff:
2025-07-02 05:45:52.740 # no non-identical "pretty close" pair
2025-07-02 05:45:52.751 if eqi is None:
2025-07-02 05:45:52.761 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:52.775 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:52.786 return
2025-07-02 05:45:52.800 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:52.811 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:52.823 else:
2025-07-02 05:45:52.835 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:52.846 eqi = None
2025-07-02 05:45:52.858
2025-07-02 05:45:52.868 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:52.876 # identical
2025-07-02 05:45:52.882
2025-07-02 05:45:52.887 # pump out diffs from before the synch point
2025-07-02 05:45:52.892 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:52.896
2025-07-02 05:45:52.901 # do intraline marking on the synch pair
2025-07-02 05:45:52.906 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:52.910 if eqi is None:
2025-07-02 05:45:52.916 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:52.928 atags = btags = ""
2025-07-02 05:45:52.937 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:52.949 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:52.959 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:52.968 if tag == 'replace':
2025-07-02 05:45:52.976 atags += '^' * la
2025-07-02 05:45:52.983 btags += '^' * lb
2025-07-02 05:45:52.990 elif tag == 'delete':
2025-07-02 05:45:52.999 atags += '-' * la
2025-07-02 05:45:53.013 elif tag == 'insert':
2025-07-02 05:45:53.027 btags += '+' * lb
2025-07-02 05:45:53.040 elif tag == 'equal':
2025-07-02 05:45:53.052 atags += ' ' * la
2025-07-02 05:45:53.064 btags += ' ' * lb
2025-07-02 05:45:53.076 else:
2025-07-02 05:45:53.085 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:53.100 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:53.113 else:
2025-07-02 05:45:53.126 # the synch pair is identical
2025-07-02 05:45:53.136 yield '  ' + aelt
2025-07-02 05:45:53.149
2025-07-02 05:45:53.160 # pump out diffs from after the synch point
2025-07-02 05:45:53.169 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:53.177
2025-07-02 05:45:53.184 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:53.191 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:53.201
2025-07-02 05:45:53.214 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:53.224 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:53.235 alo = 29, ahi = 1101
2025-07-02 05:45:53.247 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:53.254 blo = 29, bhi = 1101
2025-07-02 05:45:53.259
2025-07-02 05:45:53.264 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:53.268 g = []
2025-07-02 05:45:53.273 if alo < ahi:
2025-07-02 05:45:53.281 if blo < bhi:
2025-07-02 05:45:53.294 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:53.302 else:
2025-07-02 05:45:53.309 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:53.315 elif blo < bhi:
2025-07-02 05:45:53.322 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:53.328
2025-07-02 05:45:53.335 >       yield from g
2025-07-02 05:45:53.346
2025-07-02 05:45:53.354 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:53.362 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:53.369
2025-07-02 05:45:53.376 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:53.390 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:53.401 alo = 29, ahi = 1101
2025-07-02 05:45:53.412 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:53.420 blo = 29, bhi = 1101
2025-07-02 05:45:53.426
2025-07-02 05:45:53.432 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:53.438 r"""
2025-07-02 05:45:53.443 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:53.451 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:53.457 synch point, and intraline difference marking is done on the
2025-07-02 05:45:53.463 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:53.471
2025-07-02 05:45:53.480 Example:
2025-07-02 05:45:53.488
2025-07-02 05:45:53.494 >>> d = Differ()
2025-07-02 05:45:53.500 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:53.505 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:53.511 >>> print(''.join(results), end="")
2025-07-02 05:45:53.517 - abcDefghiJkl
2025-07-02 05:45:53.527 + abcdefGhijkl
2025-07-02 05:45:53.547 """
2025-07-02 05:45:53.555
2025-07-02 05:45:53.562 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:53.568 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:53.573 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:53.579 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:53.586 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:53.594
2025-07-02 05:45:53.606 # search for the pair that matches best without being identical
2025-07-02 05:45:53.617 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:53.629 # on junk -- unless we have to)
2025-07-02 05:45:53.638 for j in range(blo, bhi):
2025-07-02 05:45:53.644 bj = b[j]
2025-07-02 05:45:53.650 cruncher.set_seq2(bj)
2025-07-02 05:45:53.656 for i in range(alo, ahi):
2025-07-02 05:45:53.663 ai = a[i]
2025-07-02 05:45:53.671 if ai == bj:
2025-07-02 05:45:53.678 if eqi is None:
2025-07-02 05:45:53.687 eqi, eqj = i, j
2025-07-02 05:45:53.700 continue
2025-07-02 05:45:53.712 cruncher.set_seq1(ai)
2025-07-02 05:45:53.722 # computing similarity is expensive, so use the quick
2025-07-02 05:45:53.729 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:53.736 # compares by a factor of 3.
2025-07-02 05:45:53.744 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:53.754 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:53.765 # of the computation is cached by cruncher
2025-07-02 05:45:53.777 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:53.789 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:53.798 cruncher.ratio() > best_ratio:
2025-07-02 05:45:53.804 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:53.812 if best_ratio < cutoff:
2025-07-02 05:45:53.819 # no non-identical "pretty close" pair
2025-07-02 05:45:53.827 if eqi is None:
2025-07-02 05:45:53.834 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:53.843 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:53.850 return
2025-07-02 05:45:53.862 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:53.873 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:53.881 else:
2025-07-02 05:45:53.887 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:53.893 eqi = None
2025-07-02 05:45:53.900
2025-07-02 05:45:53.907 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:53.914 # identical
2025-07-02 05:45:53.920
2025-07-02 05:45:53.927 # pump out diffs from before the synch point
2025-07-02 05:45:53.934 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:53.939
2025-07-02 05:45:53.944 # do intraline marking on the synch pair
2025-07-02 05:45:53.949 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:53.955 if eqi is None:
2025-07-02 05:45:53.961 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:53.967 atags = btags = ""
2025-07-02 05:45:53.973 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:53.980 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:53.987 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:53.994 if tag == 'replace':
2025-07-02 05:45:54.002 atags += '^' * la
2025-07-02 05:45:54.013 btags += '^' * lb
2025-07-02 05:45:54.027 elif tag == 'delete':
2025-07-02 05:45:54.037 atags += '-' * la
2025-07-02 05:45:54.045 elif tag == 'insert':
2025-07-02 05:45:54.052 btags += '+' * lb
2025-07-02 05:45:54.058 elif tag == 'equal':
2025-07-02 05:45:54.066 atags += ' ' * la
2025-07-02 05:45:54.076 btags += ' ' * lb
2025-07-02 05:45:54.084 else:
2025-07-02 05:45:54.091 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:54.097 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:54.103 else:
2025-07-02 05:45:54.109 # the synch pair is identical
2025-07-02 05:45:54.116 yield '  ' + aelt
2025-07-02 05:45:54.122
2025-07-02 05:45:54.134 # pump out diffs from after the synch point
2025-07-02 05:45:54.143 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:54.149
2025-07-02 05:45:54.155 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:54.160 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:54.165
2025-07-02 05:45:54.170 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:54.177 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:54.183 alo = 30, ahi = 1101
2025-07-02 05:45:54.190 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:54.198 blo = 30, bhi = 1101
2025-07-02 05:45:54.205
2025-07-02 05:45:54.211 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:54.217 g = []
2025-07-02 05:45:54.223 if alo < ahi:
2025-07-02 05:45:54.229 if blo < bhi:
2025-07-02 05:45:54.235 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:54.244 else:
2025-07-02 05:45:54.252 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:54.263 elif blo < bhi:
2025-07-02 05:45:54.269 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:54.274
2025-07-02 05:45:54.279 >       yield from g
2025-07-02 05:45:54.286
2025-07-02 05:45:54.297 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:54.305 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:54.312
2025-07-02 05:45:54.319 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:54.328 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:54.334 alo = 30, ahi = 1101
2025-07-02 05:45:54.343 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:54.351 blo = 30, bhi = 1101
2025-07-02 05:45:54.364
2025-07-02 05:45:54.377 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:54.387 r"""
2025-07-02 05:45:54.399 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:54.409 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:54.421 synch point, and intraline difference marking is done on the
2025-07-02 05:45:54.433 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:54.443
2025-07-02 05:45:54.451 Example:
2025-07-02 05:45:54.462
2025-07-02 05:45:54.475 >>> d = Differ()
2025-07-02 05:45:54.486 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:54.495 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:54.501 >>> print(''.join(results), end="")
2025-07-02 05:45:54.507 - abcDefghiJkl
2025-07-02 05:45:54.517 + abcdefGhijkl
2025-07-02 05:45:54.527 """
2025-07-02 05:45:54.540
2025-07-02 05:45:54.550 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:54.559 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:54.567 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:54.579 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:54.590 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:54.600
2025-07-02 05:45:54.613 # search for the pair that matches best without being identical
2025-07-02 05:45:54.625 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:54.637 # on junk -- unless we have to)
2025-07-02 05:45:54.646 for j in range(blo, bhi):
2025-07-02 05:45:54.654 bj = b[j]
2025-07-02 05:45:54.667 cruncher.set_seq2(bj)
2025-07-02 05:45:54.678 for i in range(alo, ahi):
2025-07-02 05:45:54.691 ai = a[i]
2025-07-02 05:45:54.703 if ai == bj:
2025-07-02 05:45:54.716 if eqi is None:
2025-07-02 05:45:54.728 eqi, eqj = i, j
2025-07-02 05:45:54.741 continue
2025-07-02 05:45:54.754 cruncher.set_seq1(ai)
2025-07-02 05:45:54.765 # computing similarity is expensive, so use the quick
2025-07-02 05:45:54.775 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:54.781 # compares by a factor of 3.
2025-07-02 05:45:54.788 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:54.796 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:54.803 # of the computation is cached by cruncher
2025-07-02 05:45:54.810 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:54.817 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:54.825 cruncher.ratio() > best_ratio:
2025-07-02 05:45:54.832 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:54.839 if best_ratio < cutoff:
2025-07-02 05:45:54.846 # no non-identical "pretty close" pair
2025-07-02 05:45:54.858 if eqi is None:
2025-07-02 05:45:54.869 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:54.882 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:54.892 return
2025-07-02 05:45:54.901 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:54.908 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:54.914 else:
2025-07-02 05:45:54.924 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:54.936 eqi = None
2025-07-02 05:45:54.950
2025-07-02 05:45:54.961 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:54.972 # identical
2025-07-02 05:45:54.986
2025-07-02 05:45:54.998 # pump out diffs from before the synch point
2025-07-02 05:45:55.010 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:55.018
2025-07-02 05:45:55.024 # do intraline marking on the synch pair
2025-07-02 05:45:55.030 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:55.036 if eqi is None:
2025-07-02 05:45:55.043 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:55.050 atags = btags = ""
2025-07-02 05:45:55.059 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:55.071 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:55.081 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:55.095 if tag == 'replace':
2025-07-02 05:45:55.107 atags += '^' * la
2025-07-02 05:45:55.116 btags += '^' * lb
2025-07-02 05:45:55.122 elif tag == 'delete':
2025-07-02 05:45:55.127 atags += '-' * la
2025-07-02 05:45:55.133 elif tag == 'insert':
2025-07-02 05:45:55.138 btags += '+' * lb
2025-07-02 05:45:55.142 elif tag == 'equal':
2025-07-02 05:45:55.150 atags += ' ' * la
2025-07-02 05:45:55.156 btags += ' ' * lb
2025-07-02 05:45:55.162 else:
2025-07-02 05:45:55.171 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:55.180 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:55.188 else:
2025-07-02 05:45:55.196 # the synch pair is identical
2025-07-02 05:45:55.202 yield '  ' + aelt
2025-07-02 05:45:55.209
2025-07-02 05:45:55.216 # pump out diffs from after the synch point
2025-07-02 05:45:55.222 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:55.227
2025-07-02 05:45:55.235 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:55.246 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:55.255
2025-07-02 05:45:55.262 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:55.272 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:55.283 alo = 31, ahi = 1101
2025-07-02 05:45:55.300 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:55.311 blo = 31, bhi = 1101
2025-07-02 05:45:55.322
2025-07-02 05:45:55.332 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:55.345 g = []
2025-07-02 05:45:55.358 if alo < ahi:
2025-07-02 05:45:55.369 if blo < bhi:
2025-07-02 05:45:55.379 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:55.388 else:
2025-07-02 05:45:55.396 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:55.403 elif blo < bhi:
2025-07-02 05:45:55.411 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:55.423
2025-07-02 05:45:55.436 >       yield from g
2025-07-02 05:45:55.446
2025-07-02 05:45:55.455 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:55.463 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:55.470
2025-07-02 05:45:55.475 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:55.481 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:55.486 alo = 31, ahi = 1101
2025-07-02 05:45:55.493 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:55.499 blo = 31, bhi = 1101
2025-07-02 05:45:55.504
2025-07-02 05:45:55.513 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:55.521 r"""
2025-07-02 05:45:55.529 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:55.537 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:55.545 synch point, and intraline difference marking is done on the
2025-07-02 05:45:55.554 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:55.563
2025-07-02 05:45:55.570 Example:
2025-07-02 05:45:55.578
2025-07-02 05:45:55.590 >>> d = Differ()
2025-07-02 05:45:55.602 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:55.612 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:55.621 >>> print(''.join(results), end="")
2025-07-02 05:45:55.628 - abcDefghiJkl
2025-07-02 05:45:55.646 + abcdefGhijkl
2025-07-02 05:45:55.664 """
2025-07-02 05:45:55.670
2025-07-02 05:45:55.678 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:55.685 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:55.692 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:55.698 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:55.707 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:55.717
2025-07-02 05:45:55.725 # search for the pair that matches best without being identical
2025-07-02 05:45:55.732 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:55.738 # on junk -- unless we have to)
2025-07-02 05:45:55.744 for j in range(blo, bhi):
2025-07-02 05:45:55.750 bj = b[j]
2025-07-02 05:45:55.755 cruncher.set_seq2(bj)
2025-07-02 05:45:55.760 for i in range(alo, ahi):
2025-07-02 05:45:55.765 ai = a[i]
2025-07-02 05:45:55.771 if ai == bj:
2025-07-02 05:45:55.776 if eqi is None:
2025-07-02 05:45:55.782 eqi, eqj = i, j
2025-07-02 05:45:55.787 continue
2025-07-02 05:45:55.794 cruncher.set_seq1(ai)
2025-07-02 05:45:55.809 # computing similarity is expensive, so use the quick
2025-07-02 05:45:55.820 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:55.829 # compares by a factor of 3.
2025-07-02 05:45:55.837 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:55.844 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:55.851 # of the computation is cached by cruncher
2025-07-02 05:45:55.858 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:55.866 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:55.873 cruncher.ratio() > best_ratio:
2025-07-02 05:45:55.879 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:55.885 if best_ratio < cutoff:
2025-07-02 05:45:55.896 # no non-identical "pretty close" pair
2025-07-02 05:45:55.906 if eqi is None:
2025-07-02 05:45:55.914 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:55.923 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:55.931 return
2025-07-02 05:45:55.939 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:55.947 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:55.956 else:
2025-07-02 05:45:55.963 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:55.972 eqi = None
2025-07-02 05:45:55.985
2025-07-02 05:45:55.995 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:56.002 # identical
2025-07-02 05:45:56.009
2025-07-02 05:45:56.014 # pump out diffs from before the synch point
2025-07-02 05:45:56.021 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:56.027
2025-07-02 05:45:56.032 # do intraline marking on the synch pair
2025-07-02 05:45:56.038 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:56.043 if eqi is None:
2025-07-02 05:45:56.049 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:56.055 atags = btags = ""
2025-07-02 05:45:56.062 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:56.071 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:56.082 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:56.090 if tag == 'replace':
2025-07-02 05:45:56.097 atags += '^' * la
2025-07-02 05:45:56.103 btags += '^' * lb
2025-07-02 05:45:56.112 elif tag == 'delete':
2025-07-02 05:45:56.123 atags += '-' * la
2025-07-02 05:45:56.132 elif tag == 'insert':
2025-07-02 05:45:56.140 btags += '+' * lb
2025-07-02 05:45:56.147 elif tag == 'equal':
2025-07-02 05:45:56.155 atags += ' ' * la
2025-07-02 05:45:56.165 btags += ' ' * lb
2025-07-02 05:45:56.174 else:
2025-07-02 05:45:56.181 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:56.188 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:56.194 else:
2025-07-02 05:45:56.206 # the synch pair is identical
2025-07-02 05:45:56.215 yield '  ' + aelt
2025-07-02 05:45:56.223
2025-07-02 05:45:56.231 # pump out diffs from after the synch point
2025-07-02 05:45:56.238 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:56.244
2025-07-02 05:45:56.250 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:56.257 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:56.262
2025-07-02 05:45:56.269 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:56.281 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:56.292 alo = 32, ahi = 1101
2025-07-02 05:45:56.305 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:56.318 blo = 32, bhi = 1101
2025-07-02 05:45:56.328
2025-07-02 05:45:56.337 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:56.345 g = []
2025-07-02 05:45:56.351 if alo < ahi:
2025-07-02 05:45:56.358 if blo < bhi:
2025-07-02 05:45:56.369 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:56.378 else:
2025-07-02 05:45:56.386 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:56.395 elif blo < bhi:
2025-07-02 05:45:56.400 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:56.406
2025-07-02 05:45:56.412 >       yield from g
2025-07-02 05:45:56.419
2025-07-02 05:45:56.432 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:56.443 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:56.452
2025-07-02 05:45:56.460 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:56.473 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:56.482 alo = 32, ahi = 1101
2025-07-02 05:45:56.492 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:56.499 blo = 32, bhi = 1101
2025-07-02 05:45:56.506
2025-07-02 05:45:56.513 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:56.519 r"""
2025-07-02 05:45:56.525 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:56.532 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:56.545 synch point, and intraline difference marking is done on the
2025-07-02 05:45:56.556 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:56.565
2025-07-02 05:45:56.572 Example:
2025-07-02 05:45:56.579
2025-07-02 05:45:56.585 >>> d = Differ()
2025-07-02 05:45:56.591 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:56.599 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:56.611 >>> print(''.join(results), end="")
2025-07-02 05:45:56.621 - abcDefghiJkl
2025-07-02 05:45:56.640 + abcdefGhijkl
2025-07-02 05:45:56.652 """
2025-07-02 05:45:56.657
2025-07-02 05:45:56.663 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:56.668 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:56.673 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:56.678 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:56.685 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:56.690
2025-07-02 05:45:56.696 # search for the pair that matches best without being identical
2025-07-02 05:45:56.703 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:56.711 # on junk -- unless we have to)
2025-07-02 05:45:56.722 for j in range(blo, bhi):
2025-07-02 05:45:56.733 bj = b[j]
2025-07-02 05:45:56.744 cruncher.set_seq2(bj)
2025-07-02 05:45:56.756 for i in range(alo, ahi):
2025-07-02 05:45:56.767 ai = a[i]
2025-07-02 05:45:56.774 if ai == bj:
2025-07-02 05:45:56.782 if eqi is None:
2025-07-02 05:45:56.790 eqi, eqj = i, j
2025-07-02 05:45:56.798 continue
2025-07-02 05:45:56.805 cruncher.set_seq1(ai)
2025-07-02 05:45:56.811 # computing similarity is expensive, so use the quick
2025-07-02 05:45:56.819 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:56.831 # compares by a factor of 3.
2025-07-02 05:45:56.839 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:56.846 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:56.859 # of the computation is cached by cruncher
2025-07-02 05:45:56.872 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:56.885 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:56.895 cruncher.ratio() > best_ratio:
2025-07-02 05:45:56.908 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:56.918 if best_ratio < cutoff:
2025-07-02 05:45:56.928 # no non-identical "pretty close" pair
2025-07-02 05:45:56.941 if eqi is None:
2025-07-02 05:45:56.953 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:56.962 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:56.974 return
2025-07-02 05:45:56.984 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:56.992 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:57.005 else:
2025-07-02 05:45:57.017 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:57.025 eqi = None
2025-07-02 05:45:57.033
2025-07-02 05:45:57.040 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:57.054 # identical
2025-07-02 05:45:57.066
2025-07-02 05:45:57.080 # pump out diffs from before the synch point
2025-07-02 05:45:57.090 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:57.097
2025-07-02 05:45:57.110 # do intraline marking on the synch pair
2025-07-02 05:45:57.120 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:57.128 if eqi is None:
2025-07-02 05:45:57.141 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:57.151 atags = btags = ""
2025-07-02 05:45:57.161 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:57.168 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:57.177 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:57.189 if tag == 'replace':
2025-07-02 05:45:57.198 atags += '^' * la
2025-07-02 05:45:57.212 btags += '^' * lb
2025-07-02 05:45:57.223 elif tag == 'delete':
2025-07-02 05:45:57.233 atags += '-' * la
2025-07-02 05:45:57.247 elif tag == 'insert':
2025-07-02 05:45:57.257 btags += '+' * lb
2025-07-02 05:45:57.265 elif tag == 'equal':
2025-07-02 05:45:57.271 atags += ' ' * la
2025-07-02 05:45:57.277 btags += ' ' * lb
2025-07-02 05:45:57.282 else:
2025-07-02 05:45:57.290 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:57.297 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:57.303 else:
2025-07-02 05:45:57.308 # the synch pair is identical
2025-07-02 05:45:57.314 yield '  ' + aelt
2025-07-02 05:45:57.319
2025-07-02 05:45:57.331 # pump out diffs from after the synch point
2025-07-02 05:45:57.343 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:57.355
2025-07-02 05:45:57.364 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:57.372 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:57.379
2025-07-02 05:45:57.388 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:57.403 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:57.416 alo = 33, ahi = 1101
2025-07-02 05:45:57.429 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:57.437 blo = 33, bhi = 1101
2025-07-02 05:45:57.442
2025-07-02 05:45:57.447 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:57.452 g = []
2025-07-02 05:45:57.457 if alo < ahi:
2025-07-02 05:45:57.462 if blo < bhi:
2025-07-02 05:45:57.467 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:57.472 else:
2025-07-02 05:45:57.478 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:57.483 elif blo < bhi:
2025-07-02 05:45:57.491 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:57.504
2025-07-02 05:45:57.513 >       yield from g
2025-07-02 05:45:57.521
2025-07-02 05:45:57.528 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:57.535 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:57.541
2025-07-02 05:45:57.547 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:57.554 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:57.561 alo = 33, ahi = 1101
2025-07-02 05:45:57.568 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:57.574 blo = 33, bhi = 1101
2025-07-02 05:45:57.580
2025-07-02 05:45:57.589 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:57.601 r"""
2025-07-02 05:45:57.612 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:57.625 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:57.635 synch point, and intraline difference marking is done on the
2025-07-02 05:45:57.647 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:57.658
2025-07-02 05:45:57.668 Example:
2025-07-02 05:45:57.676
2025-07-02 05:45:57.688 >>> d = Differ()
2025-07-02 05:45:57.700 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:57.709 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:57.719 >>> print(''.join(results), end="")
2025-07-02 05:45:57.731 - abcDefghiJkl
2025-07-02 05:45:57.748 + abcdefGhijkl
2025-07-02 05:45:57.762 """
2025-07-02 05:45:57.768
2025-07-02 05:45:57.774 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:57.782 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:57.794 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:57.806 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:57.814 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:57.820
2025-07-02 05:45:57.830 # search for the pair that matches best without being identical
2025-07-02 05:45:57.838 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:57.847 # on junk -- unless we have to)
2025-07-02 05:45:57.854 for j in range(blo, bhi):
2025-07-02 05:45:57.862 bj = b[j]
2025-07-02 05:45:57.868 cruncher.set_seq2(bj)
2025-07-02 05:45:57.874 for i in range(alo, ahi):
2025-07-02 05:45:57.885 ai = a[i]
2025-07-02 05:45:57.894 if ai == bj:
2025-07-02 05:45:57.902 if eqi is None:
2025-07-02 05:45:57.909 eqi, eqj = i, j
2025-07-02 05:45:57.915 continue
2025-07-02 05:45:57.920 cruncher.set_seq1(ai)
2025-07-02 05:45:57.926 # computing similarity is expensive, so use the quick
2025-07-02 05:45:57.931 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:57.935 # compares by a factor of 3.
2025-07-02 05:45:57.940 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:57.946 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:57.952 # of the computation is cached by cruncher
2025-07-02 05:45:57.958 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:57.963 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:57.969 cruncher.ratio() > best_ratio:
2025-07-02 05:45:57.978 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:57.991 if best_ratio < cutoff:
2025-07-02 05:45:58.000 # no non-identical "pretty close" pair
2025-07-02 05:45:58.008 if eqi is None:
2025-07-02 05:45:58.016 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:58.023 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:58.029 return
2025-07-02 05:45:58.035 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:58.041 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:58.054 else:
2025-07-02 05:45:58.066 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:58.075 eqi = None
2025-07-02 05:45:58.083
2025-07-02 05:45:58.090 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:58.096 # identical
2025-07-02 05:45:58.102
2025-07-02 05:45:58.108 # pump out diffs from before the synch point
2025-07-02 05:45:58.114 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:58.120
2025-07-02 05:45:58.126 # do intraline marking on the synch pair
2025-07-02 05:45:58.133 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:58.139 if eqi is None:
2025-07-02 05:45:58.147 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:58.158 atags = btags = ""
2025-07-02 05:45:58.167 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:58.174 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:58.181 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:58.193 if tag == 'replace':
2025-07-02 05:45:58.201 atags += '^' * la
2025-07-02 05:45:58.207 btags += '^' * lb
2025-07-02 05:45:58.212 elif tag == 'delete':
2025-07-02 05:45:58.218 atags += '-' * la
2025-07-02 05:45:58.229 elif tag == 'insert':
2025-07-02 05:45:58.236 btags += '+' * lb
2025-07-02 05:45:58.249 elif tag == 'equal':
2025-07-02 05:45:58.262 atags += ' ' * la
2025-07-02 05:45:58.272 btags += ' ' * lb
2025-07-02 05:45:58.287 else:
2025-07-02 05:45:58.297 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:58.304 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:58.311 else:
2025-07-02 05:45:58.318 # the synch pair is identical
2025-07-02 05:45:58.325 yield '  ' + aelt
2025-07-02 05:45:58.331
2025-07-02 05:45:58.339 # pump out diffs from after the synch point
2025-07-02 05:45:58.347 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:58.355
2025-07-02 05:45:58.361 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:58.375 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:58.381
2025-07-02 05:45:58.390 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:58.398 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:58.405 alo = 34, ahi = 1101
2025-07-02 05:45:58.412 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:58.422 blo = 34, bhi = 1101
2025-07-02 05:45:58.429
2025-07-02 05:45:58.436 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:58.443 g = []
2025-07-02 05:45:58.449 if alo < ahi:
2025-07-02 05:45:58.455 if blo < bhi:
2025-07-02 05:45:58.462 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:58.469 else:
2025-07-02 05:45:58.479 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:58.490 elif blo < bhi:
2025-07-02 05:45:58.498 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:58.506
2025-07-02 05:45:58.517 >       yield from g
2025-07-02 05:45:58.527
2025-07-02 05:45:58.539 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:58.550 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:58.560
2025-07-02 05:45:58.569 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:58.577 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:58.583 alo = 34, ahi = 1101
2025-07-02 05:45:58.590 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:58.599 blo = 34, bhi = 1101
2025-07-02 05:45:58.607
2025-07-02 05:45:58.615 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:58.623 r"""
2025-07-02 05:45:58.636 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:58.645 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:58.653 synch point, and intraline difference marking is done on the
2025-07-02 05:45:58.660 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:58.667
2025-07-02 05:45:58.673 Example:
2025-07-02 05:45:58.687
2025-07-02 05:45:58.697 >>> d = Differ()
2025-07-02 05:45:58.709 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:58.720 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:58.729 >>> print(''.join(results), end="")
2025-07-02 05:45:58.741 - abcDefghiJkl
2025-07-02 05:45:58.761 + abcdefGhijkl
2025-07-02 05:45:58.775 """
2025-07-02 05:45:58.783
2025-07-02 05:45:58.796 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:45:58.806 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:45:58.814 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:45:58.824 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:45:58.834 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:45:58.842
2025-07-02 05:45:58.851 # search for the pair that matches best without being identical
2025-07-02 05:45:58.865 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:45:58.875 # on junk -- unless we have to)
2025-07-02 05:45:58.884 for j in range(blo, bhi):
2025-07-02 05:45:58.892 bj = b[j]
2025-07-02 05:45:58.900 cruncher.set_seq2(bj)
2025-07-02 05:45:58.906 for i in range(alo, ahi):
2025-07-02 05:45:58.914 ai = a[i]
2025-07-02 05:45:58.927 if ai == bj:
2025-07-02 05:45:58.940 if eqi is None:
2025-07-02 05:45:58.954 eqi, eqj = i, j
2025-07-02 05:45:58.967 continue
2025-07-02 05:45:58.978 cruncher.set_seq1(ai)
2025-07-02 05:45:58.988 # computing similarity is expensive, so use the quick
2025-07-02 05:45:59.002 # upper bounds first -- have seen this speed up messy
2025-07-02 05:45:59.012 # compares by a factor of 3.
2025-07-02 05:45:59.022 # note that ratio() is only expensive to compute the first
2025-07-02 05:45:59.034 # time it's called on a sequence pair; the expensive part
2025-07-02 05:45:59.044 # of the computation is cached by cruncher
2025-07-02 05:45:59.053 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:45:59.064 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:45:59.074 cruncher.ratio() > best_ratio:
2025-07-02 05:45:59.085 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:45:59.094 if best_ratio < cutoff:
2025-07-02 05:45:59.102 # no non-identical "pretty close" pair
2025-07-02 05:45:59.109 if eqi is None:
2025-07-02 05:45:59.118 # no identical pair either -- treat it as a straight replace
2025-07-02 05:45:59.127 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:59.137 return
2025-07-02 05:45:59.146 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:45:59.161 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:45:59.173 else:
2025-07-02 05:45:59.182 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:45:59.188 eqi = None
2025-07-02 05:45:59.200
2025-07-02 05:45:59.210 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:45:59.218 # identical
2025-07-02 05:45:59.227
2025-07-02 05:45:59.237 # pump out diffs from before the synch point
2025-07-02 05:45:59.246 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:45:59.255
2025-07-02 05:45:59.264 # do intraline marking on the synch pair
2025-07-02 05:45:59.272 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:45:59.279 if eqi is None:
2025-07-02 05:45:59.289 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:45:59.298 atags = btags = ""
2025-07-02 05:45:59.306 cruncher.set_seqs(aelt, belt)
2025-07-02 05:45:59.314 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:45:59.322 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:45:59.331 if tag == 'replace':
2025-07-02 05:45:59.341 atags += '^' * la
2025-07-02 05:45:59.352 btags += '^' * lb
2025-07-02 05:45:59.360 elif tag == 'delete':
2025-07-02 05:45:59.371 atags += '-' * la
2025-07-02 05:45:59.377 elif tag == 'insert':
2025-07-02 05:45:59.385 btags += '+' * lb
2025-07-02 05:45:59.391 elif tag == 'equal':
2025-07-02 05:45:59.399 atags += ' ' * la
2025-07-02 05:45:59.406 btags += ' ' * lb
2025-07-02 05:45:59.411 else:
2025-07-02 05:45:59.416 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:45:59.421 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:45:59.427 else:
2025-07-02 05:45:59.436 # the synch pair is identical
2025-07-02 05:45:59.447 yield '  ' + aelt
2025-07-02 05:45:59.457
2025-07-02 05:45:59.465 # pump out diffs from after the synch point
2025-07-02 05:45:59.476 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:45:59.486
2025-07-02 05:45:59.496 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:45:59.508 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:59.517
2025-07-02 05:45:59.527 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:59.539 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:59.548 alo = 35, ahi = 1101
2025-07-02 05:45:59.556 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:59.563 blo = 35, bhi = 1101
2025-07-02 05:45:59.568
2025-07-02 05:45:59.574 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:59.584 g = []
2025-07-02 05:45:59.594 if alo < ahi:
2025-07-02 05:45:59.602 if blo < bhi:
2025-07-02 05:45:59.613 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:45:59.626 else:
2025-07-02 05:45:59.638 g = self._dump('-', a, alo, ahi)
2025-07-02 05:45:59.651 elif blo < bhi:
2025-07-02 05:45:59.661 g = self._dump('+', b, blo, bhi)
2025-07-02 05:45:59.673
2025-07-02 05:45:59.684 >       yield from g
2025-07-02 05:45:59.697
2025-07-02 05:45:59.708 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:45:59.720 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:45:59.729
2025-07-02 05:45:59.739 self = <difflib.Differ object at [hex]>
2025-07-02 05:45:59.749 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:45:59.762 alo = 35, ahi = 1101
2025-07-02 05:45:59.775 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:45:59.790 blo = 35, bhi = 1101
2025-07-02 05:45:59.802
2025-07-02 05:45:59.812 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:45:59.820 r"""
2025-07-02 05:45:59.832 When replacing one block of lines with another, search the blocks
2025-07-02 05:45:59.845 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:45:59.855 synch point, and intraline difference marking is done on the
2025-07-02 05:45:59.864 similar pair. Lots of work, but often worth it.
2025-07-02 05:45:59.872
2025-07-02 05:45:59.881 Example:
2025-07-02 05:45:59.892
2025-07-02 05:45:59.900 >>> d = Differ()
2025-07-02 05:45:59.913 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:45:59.925 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:45:59.936 >>> print(''.join(results), end="")
2025-07-02 05:45:59.945 - abcDefghiJkl
2025-07-02 05:45:59.959 + abcdefGhijkl
2025-07-02 05:45:59.980 """
2025-07-02 05:45:59.990
2025-07-02 05:46:00.000 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:00.013 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:00.024 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:00.037 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:00.047 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:00.055
2025-07-02 05:46:00.062 # search for the pair that matches best without being identical
2025-07-02 05:46:00.069 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:00.083 # on junk -- unless we have to)
2025-07-02 05:46:00.093 for j in range(blo, bhi):
2025-07-02 05:46:00.100 bj = b[j]
2025-07-02 05:46:00.107 cruncher.set_seq2(bj)
2025-07-02 05:46:00.114 for i in range(alo, ahi):
2025-07-02 05:46:00.125 ai = a[i]
2025-07-02 05:46:00.135 if ai == bj:
2025-07-02 05:46:00.144 if eqi is None:
2025-07-02 05:46:00.151 eqi, eqj = i, j
2025-07-02 05:46:00.163 continue
2025-07-02 05:46:00.173 cruncher.set_seq1(ai)
2025-07-02 05:46:00.181 # computing similarity is expensive, so use the quick
2025-07-02 05:46:00.188 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:00.195 # compares by a factor of 3.
2025-07-02 05:46:00.200 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:00.207 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:00.215 # of the computation is cached by cruncher
2025-07-02 05:46:00.227 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:00.236 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:00.244 cruncher.ratio() > best_ratio:
2025-07-02 05:46:00.250 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:00.256 if best_ratio < cutoff:
2025-07-02 05:46:00.263 # no non-identical "pretty close" pair
2025-07-02 05:46:00.269 if eqi is None:
2025-07-02 05:46:00.274 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:00.279 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:00.285 return
2025-07-02 05:46:00.290 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:00.297 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:00.302 else:
2025-07-02 05:46:00.314 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:00.322 eqi = None
2025-07-02 05:46:00.330
2025-07-02 05:46:00.339 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:00.351 # identical
2025-07-02 05:46:00.363
2025-07-02 05:46:00.372 # pump out diffs from before the synch point
2025-07-02 05:46:00.380 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:00.386
2025-07-02 05:46:00.393 # do intraline marking on the synch pair
2025-07-02 05:46:00.406 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:00.420 if eqi is None:
2025-07-02 05:46:00.430 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:00.439 atags = btags = ""
2025-07-02 05:46:00.450 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:00.460 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:00.471 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:00.483 if tag == 'replace':
2025-07-02 05:46:00.495 atags += '^' * la
2025-07-02 05:46:00.504 btags += '^' * lb
2025-07-02 05:46:00.512 elif tag == 'delete':
2025-07-02 05:46:00.519 atags += '-' * la
2025-07-02 05:46:00.525 elif tag == 'insert':
2025-07-02 05:46:00.531 btags += '+' * lb
2025-07-02 05:46:00.537 elif tag == 'equal':
2025-07-02 05:46:00.543 atags += ' ' * la
2025-07-02 05:46:00.551 btags += ' ' * lb
2025-07-02 05:46:00.562 else:
2025-07-02 05:46:00.572 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:00.580 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:00.586 else:
2025-07-02 05:46:00.597 # the synch pair is identical
2025-07-02 05:46:00.606 yield '  ' + aelt
2025-07-02 05:46:00.614
2025-07-02 05:46:00.623 # pump out diffs from after the synch point
2025-07-02 05:46:00.631 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:00.637
2025-07-02 05:46:00.643 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:00.651 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:00.661
2025-07-02 05:46:00.669 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:00.679 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:00.690 alo = 36, ahi = 1101
2025-07-02 05:46:00.699 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:00.706 blo = 36, bhi = 1101
2025-07-02 05:46:00.717
2025-07-02 05:46:00.728 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:00.737 g = []
2025-07-02 05:46:00.744 if alo < ahi:
2025-07-02 05:46:00.751 if blo < bhi:
2025-07-02 05:46:00.757 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:00.762 else:
2025-07-02 05:46:00.768 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:00.774 elif blo < bhi:
2025-07-02 05:46:00.785 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:00.792
2025-07-02 05:46:00.798 >       yield from g
2025-07-02 05:46:00.809
2025-07-02 05:46:00.819 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:00.829 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:00.843
2025-07-02 05:46:00.856 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:00.864 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:00.876 alo = 36, ahi = 1101
2025-07-02 05:46:00.888 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:00.898 blo = 36, bhi = 1101
2025-07-02 05:46:00.910
2025-07-02 05:46:00.921 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:00.933 r"""
2025-07-02 05:46:00.945 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:00.958 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:00.968 synch point, and intraline difference marking is done on the
2025-07-02 05:46:00.982 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:00.995
2025-07-02 05:46:01.007 Example:
2025-07-02 05:46:01.017
2025-07-02 05:46:01.027 >>> d = Differ()
2025-07-02 05:46:01.039 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:01.048 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:01.057 >>> print(''.join(results), end="")
2025-07-02 05:46:01.064 - abcDefghiJkl
2025-07-02 05:46:01.078 + abcdefGhijkl
2025-07-02 05:46:01.103 """
2025-07-02 05:46:01.111
2025-07-02 05:46:01.118 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:01.125 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:01.132 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:01.138 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:01.145 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:01.154
2025-07-02 05:46:01.164 # search for the pair that matches best without being identical
2025-07-02 05:46:01.174 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:01.183 # on junk -- unless we have to)
2025-07-02 05:46:01.194 for j in range(blo, bhi):
2025-07-02 05:46:01.206 bj = b[j]
2025-07-02 05:46:01.215 cruncher.set_seq2(bj)
2025-07-02 05:46:01.223 for i in range(alo, ahi):
2025-07-02 05:46:01.231 ai = a[i]
2025-07-02 05:46:01.238 if ai == bj:
2025-07-02 05:46:01.245 if eqi is None:
2025-07-02 05:46:01.252 eqi, eqj = i, j
2025-07-02 05:46:01.259 continue
2025-07-02 05:46:01.267 cruncher.set_seq1(ai)
2025-07-02 05:46:01.273 # computing similarity is expensive, so use the quick
2025-07-02 05:46:01.280 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:01.287 # compares by a factor of 3.
2025-07-02 05:46:01.294 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:01.301 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:01.308 # of the computation is cached by cruncher
2025-07-02 05:46:01.316 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:01.324 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:01.331 cruncher.ratio() > best_ratio:
2025-07-02 05:46:01.339 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:01.347 if best_ratio < cutoff:
2025-07-02 05:46:01.355 # no non-identical "pretty close" pair
2025-07-02 05:46:01.362 if eqi is None:
2025-07-02 05:46:01.371 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:01.379 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:01.387 return
2025-07-02 05:46:01.394 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:01.403 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:01.410 else:
2025-07-02 05:46:01.416 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:01.422 eqi = None
2025-07-02 05:46:01.429
2025-07-02 05:46:01.438 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:01.452 # identical
2025-07-02 05:46:01.465
2025-07-02 05:46:01.474 # pump out diffs from before the synch point
2025-07-02 05:46:01.480 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:01.490
2025-07-02 05:46:01.499 # do intraline marking on the synch pair
2025-07-02 05:46:01.511 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:01.520 if eqi is None:
2025-07-02 05:46:01.526 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:01.533 atags = btags = ""
2025-07-02 05:46:01.538 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:01.544 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:01.550 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:01.556 if tag == 'replace':
2025-07-02 05:46:01.563 atags += '^' * la
2025-07-02 05:46:01.572 btags += '^' * lb
2025-07-02 05:46:01.583 elif tag == 'delete':
2025-07-02 05:46:01.592 atags += '-' * la
2025-07-02 05:46:01.599 elif tag == 'insert':
2025-07-02 05:46:01.607 btags += '+' * lb
2025-07-02 05:46:01.615 elif tag == 'equal':
2025-07-02 05:46:01.623 atags += ' ' * la
2025-07-02 05:46:01.633 btags += ' ' * lb
2025-07-02 05:46:01.646 else:
2025-07-02 05:46:01.660 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:01.669 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:01.677 else:
2025-07-02 05:46:01.684 # the synch pair is identical
2025-07-02 05:46:01.692 yield '  ' + aelt
2025-07-02 05:46:01.700
2025-07-02 05:46:01.708 # pump out diffs from after the synch point
2025-07-02 05:46:01.716 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:01.723
2025-07-02 05:46:01.731 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:01.738 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:01.747
2025-07-02 05:46:01.756 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:01.763 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:01.769 alo = 37, ahi = 1101
2025-07-02 05:46:01.775 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:01.780 blo = 37, bhi = 1101
2025-07-02 05:46:01.791
2025-07-02 05:46:01.799 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:01.806 g = []
2025-07-02 05:46:01.813 if alo < ahi:
2025-07-02 05:46:01.821 if blo < bhi:
2025-07-02 05:46:01.833 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:01.840 else:
2025-07-02 05:46:01.846 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:01.855 elif blo < bhi:
2025-07-02 05:46:01.867 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:01.875
2025-07-02 05:46:01.883 >       yield from g
2025-07-02 05:46:01.890
2025-07-02 05:46:01.896 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:01.903 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:01.914
2025-07-02 05:46:01.923 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:01.933 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:01.943 alo = 37, ahi = 1101
2025-07-02 05:46:01.952 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:01.960 blo = 37, bhi = 1101
2025-07-02 05:46:01.967
2025-07-02 05:46:01.976 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:01.982 r"""
2025-07-02 05:46:01.989 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:01.997 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:02.005 synch point, and intraline difference marking is done on the
2025-07-02 05:46:02.013 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:02.020
2025-07-02 05:46:02.027 Example:
2025-07-02 05:46:02.035
2025-07-02 05:46:02.042 >>> d = Differ()
2025-07-02 05:46:02.050 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:02.058 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:02.065 >>> print(''.join(results), end="")
2025-07-02 05:46:02.072 - abcDefghiJkl
2025-07-02 05:46:02.087 + abcdefGhijkl
2025-07-02 05:46:02.111 """
2025-07-02 05:46:02.119
2025-07-02 05:46:02.127 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:02.135 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:02.144 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:02.157 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:02.165 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:02.172
2025-07-02 05:46:02.178 # search for the pair that matches best without being identical
2025-07-02 05:46:02.184 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:02.189 # on junk -- unless we have to)
2025-07-02 05:46:02.200 for j in range(blo, bhi):
2025-07-02 05:46:02.212 bj = b[j]
2025-07-02 05:46:02.222 cruncher.set_seq2(bj)
2025-07-02 05:46:02.229 for i in range(alo, ahi):
2025-07-02 05:46:02.237 ai = a[i]
2025-07-02 05:46:02.244 if ai == bj:
2025-07-02 05:46:02.253 if eqi is None:
2025-07-02 05:46:02.262 eqi, eqj = i, j
2025-07-02 05:46:02.271 continue
2025-07-02 05:46:02.282 cruncher.set_seq1(ai)
2025-07-02 05:46:02.287 # computing similarity is expensive, so use the quick
2025-07-02 05:46:02.293 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:02.298 # compares by a factor of 3.
2025-07-02 05:46:02.306 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:02.313 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:02.319 # of the computation is cached by cruncher
2025-07-02 05:46:02.324 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:02.331 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:02.341 cruncher.ratio() > best_ratio:
2025-07-02 05:46:02.349 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:02.356 if best_ratio < cutoff:
2025-07-02 05:46:02.366 # no non-identical "pretty close" pair
2025-07-02 05:46:02.373 if eqi is None:
2025-07-02 05:46:02.381 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:02.388 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:02.394 return
2025-07-02 05:46:02.405 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:02.416 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:02.424 else:
2025-07-02 05:46:02.432 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:02.444 eqi = None
2025-07-02 05:46:02.453
2025-07-02 05:46:02.461 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:02.468 # identical
2025-07-02 05:46:02.475
2025-07-02 05:46:02.482 # pump out diffs from before the synch point
2025-07-02 05:46:02.490 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:02.497
2025-07-02 05:46:02.504 # do intraline marking on the synch pair
2025-07-02 05:46:02.511 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:02.519 if eqi is None:
2025-07-02 05:46:02.527 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:02.535 atags = btags = ""
2025-07-02 05:46:02.542 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:02.550 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:02.556 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:02.564 if tag == 'replace':
2025-07-02 05:46:02.573 atags += '^' * la
2025-07-02 05:46:02.580 btags += '^' * lb
2025-07-02 05:46:02.587 elif tag == 'delete':
2025-07-02 05:46:02.594 atags += '-' * la
2025-07-02 05:46:02.603 elif tag == 'insert':
2025-07-02 05:46:02.611 btags += '+' * lb
2025-07-02 05:46:02.618 elif tag == 'equal':
2025-07-02 05:46:02.625 atags += ' ' * la
2025-07-02 05:46:02.632 btags += ' ' * lb
2025-07-02 05:46:02.639 else:
2025-07-02 05:46:02.647 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:02.655 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:02.666 else:
2025-07-02 05:46:02.675 # the synch pair is identical
2025-07-02 05:46:02.685 yield '  ' + aelt
2025-07-02 05:46:02.692
2025-07-02 05:46:02.698 # pump out diffs from after the synch point
2025-07-02 05:46:02.704 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:02.710
2025-07-02 05:46:02.717 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:02.730 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:02.736
2025-07-02 05:46:02.742 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:02.750 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:02.757 alo = 38, ahi = 1101
2025-07-02 05:46:02.764 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:02.770 blo = 38, bhi = 1101
2025-07-02 05:46:02.775
2025-07-02 05:46:02.781 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:02.785 g = []
2025-07-02 05:46:02.791 if alo < ahi:
2025-07-02 05:46:02.796 if blo < bhi:
2025-07-02 05:46:02.802 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:02.807 else:
2025-07-02 05:46:02.813 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:02.818 elif blo < bhi:
2025-07-02 05:46:02.823 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:02.828
2025-07-02 05:46:02.833 >       yield from g
2025-07-02 05:46:02.839
2025-07-02 05:46:02.848 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:02.855 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:02.867
2025-07-02 05:46:02.877 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:02.885 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:02.892 alo = 38, ahi = 1101
2025-07-02 05:46:02.903 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:02.914 blo = 38, bhi = 1101
2025-07-02 05:46:02.922
2025-07-02 05:46:02.928 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:02.934 r"""
2025-07-02 05:46:02.941 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:02.948 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:02.955 synch point, and intraline difference marking is done on the
2025-07-02 05:46:02.962 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:02.969
2025-07-02 05:46:02.975 Example:
2025-07-02 05:46:02.981
2025-07-02 05:46:02.987 >>> d = Differ()
2025-07-02 05:46:03.000 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:03.009 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:03.017 >>> print(''.join(results), end="")
2025-07-02 05:46:03.024 - abcDefghiJkl
2025-07-02 05:46:03.036 + abcdefGhijkl
2025-07-02 05:46:03.048 """
2025-07-02 05:46:03.054
2025-07-02 05:46:03.063 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:03.074 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:03.086 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:03.096 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:03.105 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:03.112
2025-07-02 05:46:03.120 # search for the pair that matches best without being identical
2025-07-02 05:46:03.128 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:03.136 # on junk -- unless we have to)
2025-07-02 05:46:03.143 for j in range(blo, bhi):
2025-07-02 05:46:03.150 bj = b[j]
2025-07-02 05:46:03.160 cruncher.set_seq2(bj)
2025-07-02 05:46:03.172 for i in range(alo, ahi):
2025-07-02 05:46:03.182 ai = a[i]
2025-07-02 05:46:03.194 if ai == bj:
2025-07-02 05:46:03.205 if eqi is None:
2025-07-02 05:46:03.215 eqi, eqj = i, j
2025-07-02 05:46:03.223 continue
2025-07-02 05:46:03.231 cruncher.set_seq1(ai)
2025-07-02 05:46:03.240 # computing similarity is expensive, so use the quick
2025-07-02 05:46:03.252 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:03.262 # compares by a factor of 3.
2025-07-02 05:46:03.272 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:03.282 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:03.295 # of the computation is cached by cruncher
2025-07-02 05:46:03.305 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:03.313 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:03.319 cruncher.ratio() > best_ratio:
2025-07-02 05:46:03.325 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:03.331 if best_ratio < cutoff:
2025-07-02 05:46:03.338 # no non-identical "pretty close" pair
2025-07-02 05:46:03.350 if eqi is None:
2025-07-02 05:46:03.362 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:03.370 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:03.377 return
2025-07-02 05:46:03.382 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:03.393 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:03.405 else:
2025-07-02 05:46:03.417 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:03.430 eqi = None
2025-07-02 05:46:03.439
2025-07-02 05:46:03.447 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:03.455 # identical
2025-07-02 05:46:03.461
2025-07-02 05:46:03.467 # pump out diffs from before the synch point
2025-07-02 05:46:03.480 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:03.495
2025-07-02 05:46:03.503 # do intraline marking on the synch pair
2025-07-02 05:46:03.509 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:03.517 if eqi is None:
2025-07-02 05:46:03.530 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:03.539 atags = btags = ""
2025-07-02 05:46:03.548 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:03.560 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:03.569 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:03.576 if tag == 'replace':
2025-07-02 05:46:03.582 atags += '^' * la
2025-07-02 05:46:03.588 btags += '^' * lb
2025-07-02 05:46:03.594 elif tag == 'delete':
2025-07-02 05:46:03.601 atags += '-' * la
2025-07-02 05:46:03.608 elif tag == 'insert':
2025-07-02 05:46:03.615 btags += '+' * lb
2025-07-02 05:46:03.623 elif tag == 'equal':
2025-07-02 05:46:03.631 atags += ' ' * la
2025-07-02 05:46:03.639 btags += ' ' * lb
2025-07-02 05:46:03.647 else:
2025-07-02 05:46:03.655 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:03.663 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:03.671 else:
2025-07-02 05:46:03.679 # the synch pair is identical
2025-07-02 05:46:03.686 yield '  ' + aelt
2025-07-02 05:46:03.698
2025-07-02 05:46:03.709 # pump out diffs from after the synch point
2025-07-02 05:46:03.719 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:03.728
2025-07-02 05:46:03.736 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:03.744 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:03.749
2025-07-02 05:46:03.755 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:03.762 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:03.768 alo = 39, ahi = 1101
2025-07-02 05:46:03.775 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:03.782 blo = 39, bhi = 1101
2025-07-02 05:46:03.790
2025-07-02 05:46:03.797 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:03.805 g = []
2025-07-02 05:46:03.810 if alo < ahi:
2025-07-02 05:46:03.816 if blo < bhi:
2025-07-02 05:46:03.822 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:03.831 else:
2025-07-02 05:46:03.838 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:03.846 elif blo < bhi:
2025-07-02 05:46:03.853 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:03.862
2025-07-02 05:46:03.868 >       yield from g
2025-07-02 05:46:03.878
2025-07-02 05:46:03.890 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:03.899 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:03.911
2025-07-02 05:46:03.920 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:03.930 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:03.937 alo = 39, ahi = 1101
2025-07-02 05:46:03.945 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:03.954 blo = 39, bhi = 1101
2025-07-02 05:46:03.964
2025-07-02 05:46:03.973 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:03.980 r"""
2025-07-02 05:46:03.986 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:03.994 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:04.001 synch point, and intraline difference marking is done on the
2025-07-02 05:46:04.009 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:04.017
2025-07-02 05:46:04.023 Example:
2025-07-02 05:46:04.030
2025-07-02 05:46:04.037 >>> d = Differ()
2025-07-02 05:46:04.044 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:04.053 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:04.061 >>> print(''.join(results), end="")
2025-07-02 05:46:04.073 - abcDefghiJkl
2025-07-02 05:46:04.090 + abcdefGhijkl
2025-07-02 05:46:04.105 """
2025-07-02 05:46:04.113
2025-07-02 05:46:04.125 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:04.138 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:04.149 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:04.162 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:04.176 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:04.194
2025-07-02 05:46:04.208 # search for the pair that matches best without being identical
2025-07-02 05:46:04.215 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:04.222 # on junk -- unless we have to)
2025-07-02 05:46:04.228 for j in range(blo, bhi):
2025-07-02 05:46:04.234 bj = b[j]
2025-07-02 05:46:04.239 cruncher.set_seq2(bj)
2025-07-02 05:46:04.245 for i in range(alo, ahi):
2025-07-02 05:46:04.252 ai = a[i]
2025-07-02 05:46:04.258 if ai == bj:
2025-07-02 05:46:04.265 if eqi is None:
2025-07-02 05:46:04.272 eqi, eqj = i, j
2025-07-02 05:46:04.279 continue
2025-07-02 05:46:04.286 cruncher.set_seq1(ai)
2025-07-02 05:46:04.294 # computing similarity is expensive, so use the quick
2025-07-02 05:46:04.302 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:04.309 # compares by a factor of 3.
2025-07-02 05:46:04.317 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:04.325 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:04.332 # of the computation is cached by cruncher
2025-07-02 05:46:04.340 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:04.348 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:04.356 cruncher.ratio() > best_ratio:
2025-07-02 05:46:04.362 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:04.369 if best_ratio < cutoff:
2025-07-02 05:46:04.376 # no non-identical "pretty close" pair
2025-07-02 05:46:04.383 if eqi is None:
2025-07-02 05:46:04.389 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:04.396 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:04.402 return
2025-07-02 05:46:04.408 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:04.414 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:04.420 else:
2025-07-02 05:46:04.427 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:04.433 eqi = None
2025-07-02 05:46:04.439
2025-07-02 05:46:04.445 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:04.451 # identical
2025-07-02 05:46:04.457
2025-07-02 05:46:04.464 # pump out diffs from before the synch point
2025-07-02 05:46:04.470 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:04.476
2025-07-02 05:46:04.483 # do intraline marking on the synch pair
2025-07-02 05:46:04.489 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:04.497 if eqi is None:
2025-07-02 05:46:04.505 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:04.513 atags = btags = ""
2025-07-02 05:46:04.519 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:04.529 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:04.537 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:04.547 if tag == 'replace':
2025-07-02 05:46:04.555 atags += '^' * la
2025-07-02 05:46:04.562 btags += '^' * lb
2025-07-02 05:46:04.569 elif tag == 'delete':
2025-07-02 05:46:04.577 atags += '-' * la
2025-07-02 05:46:04.583 elif tag == 'insert':
2025-07-02 05:46:04.589 btags += '+' * lb
2025-07-02 05:46:04.595 elif tag == 'equal':
2025-07-02 05:46:04.601 atags += ' ' * la
2025-07-02 05:46:04.607 btags += ' ' * lb
2025-07-02 05:46:04.613 else:
2025-07-02 05:46:04.619 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:04.625 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:04.637 else:
2025-07-02 05:46:04.648 # the synch pair is identical
2025-07-02 05:46:04.661 yield '  ' + aelt
2025-07-02 05:46:04.669
2025-07-02 05:46:04.675 # pump out diffs from after the synch point
2025-07-02 05:46:04.682 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:04.688
2025-07-02 05:46:04.694 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:04.701 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:04.708
2025-07-02 05:46:04.719 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:04.729 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:04.736 alo = 40, ahi = 1101
2025-07-02 05:46:04.743 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:04.748 blo = 40, bhi = 1101
2025-07-02 05:46:04.753
2025-07-02 05:46:04.759 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:04.763 g = []
2025-07-02 05:46:04.768 if alo < ahi:
2025-07-02 05:46:04.773 if blo < bhi:
2025-07-02 05:46:04.778 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:04.783 else:
2025-07-02 05:46:04.789 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:04.795 elif blo < bhi:
2025-07-02 05:46:04.801 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:04.807
2025-07-02 05:46:04.814 >       yield from g
2025-07-02 05:46:04.821
2025-07-02 05:46:04.828 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:04.836 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:04.843
2025-07-02 05:46:04.850 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:04.859 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:04.867 alo = 40, ahi = 1101
2025-07-02 05:46:04.876 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:04.884 blo = 40, bhi = 1101
2025-07-02 05:46:04.891
2025-07-02 05:46:04.903 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:04.913 r"""
2025-07-02 05:46:04.921 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:04.928 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:04.935 synch point, and intraline difference marking is done on the
2025-07-02 05:46:04.943 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:04.950
2025-07-02 05:46:04.957 Example:
2025-07-02 05:46:04.964
2025-07-02 05:46:04.972 >>> d = Differ()
2025-07-02 05:46:04.980 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:04.989 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:04.997 >>> print(''.join(results), end="")
2025-07-02 05:46:05.005 - abcDefghiJkl
2025-07-02 05:46:05.021 + abcdefGhijkl
2025-07-02 05:46:05.036 """
2025-07-02 05:46:05.043
2025-07-02 05:46:05.052 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:05.060 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:05.068 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:05.076 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:05.083 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:05.091
2025-07-02 05:46:05.098 # search for the pair that matches best without being identical
2025-07-02 05:46:05.106 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:05.120 # on junk -- unless we have to)
2025-07-02 05:46:05.130 for j in range(blo, bhi):
2025-07-02 05:46:05.141 bj = b[j]
2025-07-02 05:46:05.152 cruncher.set_seq2(bj)
2025-07-02 05:46:05.161 for i in range(alo, ahi):
2025-07-02 05:46:05.168 ai = a[i]
2025-07-02 05:46:05.174 if ai == bj:
2025-07-02 05:46:05.183 if eqi is None:
2025-07-02 05:46:05.193 eqi, eqj = i, j
2025-07-02 05:46:05.201 continue
2025-07-02 05:46:05.207 cruncher.set_seq1(ai)
2025-07-02 05:46:05.212 # computing similarity is expensive, so use the quick
2025-07-02 05:46:05.217 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:05.222 # compares by a factor of 3.
2025-07-02 05:46:05.228 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:05.233 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:05.239 # of the computation is cached by cruncher
2025-07-02 05:46:05.244 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:05.250 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:05.255 cruncher.ratio() > best_ratio:
2025-07-02 05:46:05.260 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:05.268 if best_ratio < cutoff:
2025-07-02 05:46:05.278 # no non-identical "pretty close" pair
2025-07-02 05:46:05.284 if eqi is None:
2025-07-02 05:46:05.293 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:05.303 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:05.310 return
2025-07-02 05:46:05.317 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:05.323 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:05.328 else:
2025-07-02 05:46:05.339 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:05.349 eqi = None
2025-07-02 05:46:05.357
2025-07-02 05:46:05.363 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:05.370 # identical
2025-07-02 05:46:05.376
2025-07-02 05:46:05.383 # pump out diffs from before the synch point
2025-07-02 05:46:05.390 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:05.396
2025-07-02 05:46:05.403 # do intraline marking on the synch pair
2025-07-02 05:46:05.410 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:05.415 if eqi is None:
2025-07-02 05:46:05.421 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:05.427 atags = btags = ""
2025-07-02 05:46:05.434 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:05.441 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:05.448 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:05.455 if tag == 'replace':
2025-07-02 05:46:05.462 atags += '^' * la
2025-07-02 05:46:05.469 btags += '^' * lb
2025-07-02 05:46:05.476 elif tag == 'delete':
2025-07-02 05:46:05.483 atags += '-' * la
2025-07-02 05:46:05.489 elif tag == 'insert':
2025-07-02 05:46:05.496 btags += '+' * lb
2025-07-02 05:46:05.509 elif tag == 'equal':
2025-07-02 05:46:05.516 atags += ' ' * la
2025-07-02 05:46:05.522 btags += ' ' * lb
2025-07-02 05:46:05.527 else:
2025-07-02 05:46:05.534 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:05.541 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:05.548 else:
2025-07-02 05:46:05.555 # the synch pair is identical
2025-07-02 05:46:05.562 yield '  ' + aelt
2025-07-02 05:46:05.569
2025-07-02 05:46:05.576 # pump out diffs from after the synch point
2025-07-02 05:46:05.584 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:05.591
2025-07-02 05:46:05.598 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:05.606 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:05.612
2025-07-02 05:46:05.618 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:05.624 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:05.630 alo = 41, ahi = 1101
2025-07-02 05:46:05.637 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:05.643 blo = 41, bhi = 1101
2025-07-02 05:46:05.649
2025-07-02 05:46:05.654 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:05.665 g = []
2025-07-02 05:46:05.674 if alo < ahi:
2025-07-02 05:46:05.680 if blo < bhi:
2025-07-02 05:46:05.687 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:05.694 else:
2025-07-02 05:46:05.705 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:05.714 elif blo < bhi:
2025-07-02 05:46:05.722 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:05.728
2025-07-02 05:46:05.734 >       yield from g
2025-07-02 05:46:05.739
2025-07-02 05:46:05.743 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:05.748 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:05.753
2025-07-02 05:46:05.758 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:05.763 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:05.768 alo = 41, ahi = 1101
2025-07-02 05:46:05.773 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:05.778 blo = 41, bhi = 1101
2025-07-02 05:46:05.782
2025-07-02 05:46:05.787 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:05.792 r"""
2025-07-02 05:46:05.797 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:05.802 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:05.806 synch point, and intraline difference marking is done on the
2025-07-02 05:46:05.811 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:05.816
2025-07-02 05:46:05.822 Example:
2025-07-02 05:46:05.828
2025-07-02 05:46:05.833 >>> d = Differ()
2025-07-02 05:46:05.840 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:05.847 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:05.853 >>> print(''.join(results), end="")
2025-07-02 05:46:05.857 - abcDefghiJkl
2025-07-02 05:46:05.867 + abcdefGhijkl
2025-07-02 05:46:05.880 """
2025-07-02 05:46:05.893
2025-07-02 05:46:05.905 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:05.918 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:05.930 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:05.941 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:05.954 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:05.965
2025-07-02 05:46:05.973 # search for the pair that matches best without being identical
2025-07-02 05:46:05.984 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:05.995 # on junk -- unless we have to)
2025-07-02 05:46:06.003 for j in range(blo, bhi):
2025-07-02 05:46:06.011 bj = b[j]
2025-07-02 05:46:06.019 cruncher.set_seq2(bj)
2025-07-02 05:46:06.032 for i in range(alo, ahi):
2025-07-02 05:46:06.043 ai = a[i]
2025-07-02 05:46:06.051 if ai == bj:
2025-07-02 05:46:06.058 if eqi is None:
2025-07-02 05:46:06.066 eqi, eqj = i, j
2025-07-02 05:46:06.076 continue
2025-07-02 05:46:06.088 cruncher.set_seq1(ai)
2025-07-02 05:46:06.097 # computing similarity is expensive, so use the quick
2025-07-02 05:46:06.105 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:06.112 # compares by a factor of 3.
2025-07-02 05:46:06.118 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:06.124 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:06.131 # of the computation is cached by cruncher
2025-07-02 05:46:06.138 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:06.150 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:06.163 cruncher.ratio() > best_ratio:
2025-07-02 05:46:06.175 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:06.188 if best_ratio < cutoff:
2025-07-02 05:46:06.199 # no non-identical "pretty close" pair
2025-07-02 05:46:06.207 if eqi is None:
2025-07-02 05:46:06.216 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:06.223 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:06.229 return
2025-07-02 05:46:06.236 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:06.245 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:06.256 else:
2025-07-02 05:46:06.266 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:06.276 eqi = None
2025-07-02 05:46:06.284
2025-07-02 05:46:06.292 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:06.298 # identical
2025-07-02 05:46:06.304
2025-07-02 05:46:06.310 # pump out diffs from before the synch point
2025-07-02 05:46:06.316 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:06.323
2025-07-02 05:46:06.330 # do intraline marking on the synch pair
2025-07-02 05:46:06.336 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:06.341 if eqi is None:
2025-07-02 05:46:06.347 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:06.355 atags = btags = ""
2025-07-02 05:46:06.366 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:06.374 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:06.383 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:06.395 if tag == 'replace':
2025-07-02 05:46:06.404 atags += '^' * la
2025-07-02 05:46:06.412 btags += '^' * lb
2025-07-02 05:46:06.419 elif tag == 'delete':
2025-07-02 05:46:06.426 atags += '-' * la
2025-07-02 05:46:06.432 elif tag == 'insert':
2025-07-02 05:46:06.439 btags += '+' * lb
2025-07-02 05:46:06.444 elif tag == 'equal':
2025-07-02 05:46:06.450 atags += ' ' * la
2025-07-02 05:46:06.455 btags += ' ' * lb
2025-07-02 05:46:06.465 else:
2025-07-02 05:46:06.478 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:06.487 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:06.495 else:
2025-07-02 05:46:06.503 # the synch pair is identical
2025-07-02 05:46:06.511 yield '  ' + aelt
2025-07-02 05:46:06.523
2025-07-02 05:46:06.534 # pump out diffs from after the synch point
2025-07-02 05:46:06.543 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:06.550
2025-07-02 05:46:06.557 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:06.563 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:06.577
2025-07-02 05:46:06.585 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:06.594 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:06.600 alo = 42, ahi = 1101
2025-07-02 05:46:06.608 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:06.613 blo = 42, bhi = 1101
2025-07-02 05:46:06.618
2025-07-02 05:46:06.623 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:06.629 g = []
2025-07-02 05:46:06.635 if alo < ahi:
2025-07-02 05:46:06.641 if blo < bhi:
2025-07-02 05:46:06.647 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:06.653 else:
2025-07-02 05:46:06.658 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:06.670 elif blo < bhi:
2025-07-02 05:46:06.681 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:06.689
2025-07-02 05:46:06.697 >       yield from g
2025-07-02 05:46:06.703
2025-07-02 05:46:06.713 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:06.724 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:06.737
2025-07-02 05:46:06.747 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:06.756 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:06.765 alo = 42, ahi = 1101
2025-07-02 05:46:06.775 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:06.783 blo = 42, bhi = 1101
2025-07-02 05:46:06.797
2025-07-02 05:46:06.809 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:06.816 r"""
2025-07-02 05:46:06.823 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:06.829 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:06.834 synch point, and intraline difference marking is done on the
2025-07-02 05:46:06.846 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:06.858
2025-07-02 05:46:06.867 Example:
2025-07-02 05:46:06.881
2025-07-02 05:46:06.891 >>> d = Differ()
2025-07-02 05:46:06.900 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:06.907 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:06.913 >>> print(''.join(results), end="")
2025-07-02 05:46:06.918 - abcDefghiJkl
2025-07-02 05:46:06.939 + abcdefGhijkl
2025-07-02 05:46:06.955 """
2025-07-02 05:46:06.963
2025-07-02 05:46:06.974 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:06.986 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:06.998 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:07.012 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:07.023 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:07.035
2025-07-02 05:46:07.047 # search for the pair that matches best without being identical
2025-07-02 05:46:07.057 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:07.070 # on junk -- unless we have to)
2025-07-02 05:46:07.080 for j in range(blo, bhi):
2025-07-02 05:46:07.087 bj = b[j]
2025-07-02 05:46:07.094 cruncher.set_seq2(bj)
2025-07-02 05:46:07.101 for i in range(alo, ahi):
2025-07-02 05:46:07.111 ai = a[i]
2025-07-02 05:46:07.124 if ai == bj:
2025-07-02 05:46:07.136 if eqi is None:
2025-07-02 05:46:07.151 eqi, eqj = i, j
2025-07-02 05:46:07.163 continue
2025-07-02 05:46:07.171 cruncher.set_seq1(ai)
2025-07-02 05:46:07.180 # computing similarity is expensive, so use the quick
2025-07-02 05:46:07.188 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:07.196 # compares by a factor of 3.
2025-07-02 05:46:07.203 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:07.210 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:07.216 # of the computation is cached by cruncher
2025-07-02 05:46:07.223 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:07.228 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:07.234 cruncher.ratio() > best_ratio:
2025-07-02 05:46:07.246 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:07.256 if best_ratio < cutoff:
2025-07-02 05:46:07.264 # no non-identical "pretty close" pair
2025-07-02 05:46:07.270 if eqi is None:
2025-07-02 05:46:07.276 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:07.283 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:07.289 return
2025-07-02 05:46:07.302 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:07.311 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:07.323 else:
2025-07-02 05:46:07.331 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:07.338 eqi = None
2025-07-02 05:46:07.344
2025-07-02 05:46:07.351 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:07.358 # identical
2025-07-02 05:46:07.364
2025-07-02 05:46:07.370 # pump out diffs from before the synch point
2025-07-02 05:46:07.375 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:07.380
2025-07-02 05:46:07.385 # do intraline marking on the synch pair
2025-07-02 05:46:07.390 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:07.395 if eqi is None:
2025-07-02 05:46:07.401 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:07.405 atags = btags = ""
2025-07-02 05:46:07.410 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:07.423 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:07.429 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:07.435 if tag == 'replace':
2025-07-02 05:46:07.443 atags += '^' * la
2025-07-02 05:46:07.452 btags += '^' * lb
2025-07-02 05:46:07.460 elif tag == 'delete':
2025-07-02 05:46:07.470 atags += '-' * la
2025-07-02 05:46:07.482 elif tag == 'insert':
2025-07-02 05:46:07.494 btags += '+' * lb
2025-07-02 05:46:07.504 elif tag == 'equal':
2025-07-02 05:46:07.511 atags += ' ' * la
2025-07-02 05:46:07.517 btags += ' ' * lb
2025-07-02 05:46:07.522 else:
2025-07-02 05:46:07.527 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:07.532 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:07.537 else:
2025-07-02 05:46:07.541 # the synch pair is identical
2025-07-02 05:46:07.546 yield '  ' + aelt
2025-07-02 05:46:07.558
2025-07-02 05:46:07.567 # pump out diffs from after the synch point
2025-07-02 05:46:07.574 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:07.581
2025-07-02 05:46:07.586 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:07.600 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:07.611
2025-07-02 05:46:07.622 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:07.636 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:07.649 alo = 43, ahi = 1101
2025-07-02 05:46:07.660 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:07.667 blo = 43, bhi = 1101
2025-07-02 05:46:07.679
2025-07-02 05:46:07.688 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:07.695 g = []
2025-07-02 05:46:07.701 if alo < ahi:
2025-07-02 05:46:07.714 if blo < bhi:
2025-07-02 05:46:07.724 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:07.732 else:
2025-07-02 05:46:07.740 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:07.747 elif blo < bhi:
2025-07-02 05:46:07.754 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:07.765
2025-07-02 05:46:07.774 >       yield from g
2025-07-02 05:46:07.781
2025-07-02 05:46:07.789 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:07.799 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:07.809
2025-07-02 05:46:07.819 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:07.831 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:07.842 alo = 43, ahi = 1101
2025-07-02 05:46:07.851 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:07.861 blo = 43, bhi = 1101
2025-07-02 05:46:07.871
2025-07-02 05:46:07.879 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:07.892 r"""
2025-07-02 05:46:07.905 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:07.915 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:07.923 synch point, and intraline difference marking is done on the
2025-07-02 05:46:07.930 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:07.938
2025-07-02 05:46:07.947 Example:
2025-07-02 05:46:07.956
2025-07-02 05:46:07.961 >>> d = Differ()
2025-07-02 05:46:07.967 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:07.972 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:07.978 >>> print(''.join(results), end="")
2025-07-02 05:46:07.989 - abcDefghiJkl
2025-07-02 05:46:08.006 + abcdefGhijkl
2025-07-02 05:46:08.023 """
2025-07-02 05:46:08.031
2025-07-02 05:46:08.042 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:08.054 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:08.065 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:08.077 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:08.088 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:08.097
2025-07-02 05:46:08.103 # search for the pair that matches best without being identical
2025-07-02 05:46:08.109 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:08.114 # on junk -- unless we have to)
2025-07-02 05:46:08.126 for j in range(blo, bhi):
2025-07-02 05:46:08.137 bj = b[j]
2025-07-02 05:46:08.145 cruncher.set_seq2(bj)
2025-07-02 05:46:08.152 for i in range(alo, ahi):
2025-07-02 05:46:08.159 ai = a[i]
2025-07-02 05:46:08.167 if ai == bj:
2025-07-02 05:46:08.173 if eqi is None:
2025-07-02 05:46:08.184 eqi, eqj = i, j
2025-07-02 05:46:08.194 continue
2025-07-02 05:46:08.203 cruncher.set_seq1(ai)
2025-07-02 05:46:08.211 # computing similarity is expensive, so use the quick
2025-07-02 05:46:08.219 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:08.227 # compares by a factor of 3.
2025-07-02 05:46:08.235 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:08.246 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:08.254 # of the computation is cached by cruncher
2025-07-02 05:46:08.264 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:08.277 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:08.291 cruncher.ratio() > best_ratio:
2025-07-02 05:46:08.303 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:08.311 if best_ratio < cutoff:
2025-07-02 05:46:08.318 # no non-identical "pretty close" pair
2025-07-02 05:46:08.329 if eqi is None:
2025-07-02 05:46:08.339 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:08.347 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:08.355 return
2025-07-02 05:46:08.366 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:08.376 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:08.385 else:
2025-07-02 05:46:08.393 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:08.406 eqi = None
2025-07-02 05:46:08.416
2025-07-02 05:46:08.425 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:08.432 # identical
2025-07-02 05:46:08.440
2025-07-02 05:46:08.446 # pump out diffs from before the synch point
2025-07-02 05:46:08.452 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:08.457
2025-07-02 05:46:08.463 # do intraline marking on the synch pair
2025-07-02 05:46:08.468 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:08.474 if eqi is None:
2025-07-02 05:46:08.482 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:08.489 atags = btags = ""
2025-07-02 05:46:08.498 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:08.510 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:08.521 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:08.532 if tag == 'replace':
2025-07-02 05:46:08.542 atags += '^' * la
2025-07-02 05:46:08.553 btags += '^' * lb
2025-07-02 05:46:08.565 elif tag == 'delete':
2025-07-02 05:46:08.575 atags += '-' * la
2025-07-02 05:46:08.587 elif tag == 'insert':
2025-07-02 05:46:08.599 btags += '+' * lb
2025-07-02 05:46:08.611 elif tag == 'equal':
2025-07-02 05:46:08.626 atags += ' ' * la
2025-07-02 05:46:08.636 btags += ' ' * lb
2025-07-02 05:46:08.644 else:
2025-07-02 05:46:08.651 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:08.664 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:08.672 else:
2025-07-02 05:46:08.685 # the synch pair is identical
2025-07-02 05:46:08.695 yield '  ' + aelt
2025-07-02 05:46:08.704
2025-07-02 05:46:08.712 # pump out diffs from after the synch point
2025-07-02 05:46:08.719 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:08.728
2025-07-02 05:46:08.741 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:08.751 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:08.761
2025-07-02 05:46:08.768 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:08.775 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:08.784 alo = 44, ahi = 1101
2025-07-02 05:46:08.793 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:08.803 blo = 44, bhi = 1101
2025-07-02 05:46:08.816
2025-07-02 05:46:08.826 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:08.834 g = []
2025-07-02 05:46:08.848 if alo < ahi:
2025-07-02 05:46:08.859 if blo < bhi:
2025-07-02 05:46:08.867 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:08.875 else:
2025-07-02 05:46:08.886 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:08.895 elif blo < bhi:
2025-07-02 05:46:08.901 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:08.906
2025-07-02 05:46:08.913 >       yield from g
2025-07-02 05:46:08.919
2025-07-02 05:46:08.925 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:08.931 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:08.937
2025-07-02 05:46:08.944 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:08.952 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:08.958 alo = 44, ahi = 1101
2025-07-02 05:46:08.969 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:08.982 blo = 44, bhi = 1101
2025-07-02 05:46:08.991
2025-07-02 05:46:09.000 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:09.013 r"""
2025-07-02 05:46:09.023 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:09.036 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:09.048 synch point, and intraline difference marking is done on the
2025-07-02 05:46:09.060 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:09.072
2025-07-02 05:46:09.083 Example:
2025-07-02 05:46:09.091
2025-07-02 05:46:09.099 >>> d = Differ()
2025-07-02 05:46:09.108 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:09.116 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:09.123 >>> print(''.join(results), end="")
2025-07-02 05:46:09.128 - abcDefghiJkl
2025-07-02 05:46:09.141 + abcdefGhijkl
2025-07-02 05:46:09.152 """
2025-07-02 05:46:09.158
2025-07-02 05:46:09.164 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:09.175 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:09.185 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:09.192 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:09.200 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:09.206
2025-07-02 05:46:09.212 # search for the pair that matches best without being identical
2025-07-02 05:46:09.219 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:09.227 # on junk -- unless we have to)
2025-07-02 05:46:09.240 for j in range(blo, bhi):
2025-07-02 05:46:09.248 bj = b[j]
2025-07-02 05:46:09.256 cruncher.set_seq2(bj)
2025-07-02 05:46:09.260 for i in range(alo, ahi):
2025-07-02 05:46:09.265 ai = a[i]
2025-07-02 05:46:09.270 if ai == bj:
2025-07-02 05:46:09.275 if eqi is None:
2025-07-02 05:46:09.279 eqi, eqj = i, j
2025-07-02 05:46:09.284 continue
2025-07-02 05:46:09.289 cruncher.set_seq1(ai)
2025-07-02 05:46:09.293 # computing similarity is expensive, so use the quick
2025-07-02 05:46:09.298 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:09.302 # compares by a factor of 3.
2025-07-02 05:46:09.307 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:09.311 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:09.320 # of the computation is cached by cruncher
2025-07-02 05:46:09.331 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:09.339 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:09.347 cruncher.ratio() > best_ratio:
2025-07-02 05:46:09.355 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:09.361 if best_ratio < cutoff:
2025-07-02 05:46:09.366 # no non-identical "pretty close" pair
2025-07-02 05:46:09.371 if eqi is None:
2025-07-02 05:46:09.376 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:09.381 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:09.387 return
2025-07-02 05:46:09.394 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:09.406 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:09.415 else:
2025-07-02 05:46:09.424 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:09.430 eqi = None
2025-07-02 05:46:09.436
2025-07-02 05:46:09.441 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:09.446 # identical
2025-07-02 05:46:09.450
2025-07-02 05:46:09.455 # pump out diffs from before the synch point
2025-07-02 05:46:09.460 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:09.464
2025-07-02 05:46:09.469 # do intraline marking on the synch pair
2025-07-02 05:46:09.481 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:09.491 if eqi is None:
2025-07-02 05:46:09.499 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:09.507 atags = btags = ""
2025-07-02 05:46:09.516 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:09.525 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:09.533 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:09.544 if tag == 'replace':
2025-07-02 05:46:09.555 atags += '^' * la
2025-07-02 05:46:09.565 btags += '^' * lb
2025-07-02 05:46:09.572 elif tag == 'delete':
2025-07-02 05:46:09.580 atags += '-' * la
2025-07-02 05:46:09.587 elif tag == 'insert':
2025-07-02 05:46:09.594 btags += '+' * lb
2025-07-02 05:46:09.607 elif tag == 'equal':
2025-07-02 05:46:09.615 atags += ' ' * la
2025-07-02 05:46:09.623 btags += ' ' * lb
2025-07-02 05:46:09.634 else:
2025-07-02 05:46:09.644 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:09.652 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:09.659 else:
2025-07-02 05:46:09.666 # the synch pair is identical
2025-07-02 05:46:09.672 yield '  ' + aelt
2025-07-02 05:46:09.685
2025-07-02 05:46:09.697 # pump out diffs from after the synch point
2025-07-02 05:46:09.711 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:09.725
2025-07-02 05:46:09.735 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:09.746 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:09.757
2025-07-02 05:46:09.767 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:09.776 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:09.783 alo = 45, ahi = 1101
2025-07-02 05:46:09.797 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:09.804 blo = 45, bhi = 1101
2025-07-02 05:46:09.810
2025-07-02 05:46:09.822 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:09.835 g = []
2025-07-02 05:46:09.846 if alo < ahi:
2025-07-02 05:46:09.855 if blo < bhi:
2025-07-02 05:46:09.863 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:09.876 else:
2025-07-02 05:46:09.890 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:09.901 elif blo < bhi:
2025-07-02 05:46:09.915 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:09.926
2025-07-02 05:46:09.937 >       yield from g
2025-07-02 05:46:09.946
2025-07-02 05:46:09.955 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:09.966 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:09.975
2025-07-02 05:46:09.983 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:09.997 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:10.007 alo = 45, ahi = 1101
2025-07-02 05:46:10.017 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:10.023 blo = 45, bhi = 1101
2025-07-02 05:46:10.034
2025-07-02 05:46:10.045 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:10.053 r"""
2025-07-02 05:46:10.063 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:10.071 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:10.078 synch point, and intraline difference marking is done on the
2025-07-02 05:46:10.083 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:10.087
2025-07-02 05:46:10.092 Example:
2025-07-02 05:46:10.100
2025-07-02 05:46:10.107 >>> d = Differ()
2025-07-02 05:46:10.115 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:10.127 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:10.137 >>> print(''.join(results), end="")
2025-07-02 05:46:10.144 - abcDefghiJkl
2025-07-02 05:46:10.156 + abcdefGhijkl
2025-07-02 05:46:10.170 """
2025-07-02 05:46:10.176
2025-07-02 05:46:10.182 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:10.188 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:10.193 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:10.198 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:10.203 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:10.207
2025-07-02 05:46:10.212 # search for the pair that matches best without being identical
2025-07-02 05:46:10.217 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:10.222 # on junk -- unless we have to)
2025-07-02 05:46:10.227 for j in range(blo, bhi):
2025-07-02 05:46:10.231 bj = b[j]
2025-07-02 05:46:10.236 cruncher.set_seq2(bj)
2025-07-02 05:46:10.242 for i in range(alo, ahi):
2025-07-02 05:46:10.249 ai = a[i]
2025-07-02 05:46:10.255 if ai == bj:
2025-07-02 05:46:10.261 if eqi is None:
2025-07-02 05:46:10.268 eqi, eqj = i, j
2025-07-02 05:46:10.275 continue
2025-07-02 05:46:10.284 cruncher.set_seq1(ai)
2025-07-02 05:46:10.293 # computing similarity is expensive, so use the quick
2025-07-02 05:46:10.300 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:10.306 # compares by a factor of 3.
2025-07-02 05:46:10.312 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:10.327 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:10.337 # of the computation is cached by cruncher
2025-07-02 05:46:10.345 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:10.352 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:10.359 cruncher.ratio() > best_ratio:
2025-07-02 05:46:10.365 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:10.375 if best_ratio < cutoff:
2025-07-02 05:46:10.382 # no non-identical "pretty close" pair
2025-07-02 05:46:10.389 if eqi is None:
2025-07-02 05:46:10.394 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:10.400 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:10.406 return
2025-07-02 05:46:10.413 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:10.420 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:10.427 else:
2025-07-02 05:46:10.438 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:10.449 eqi = None
2025-07-02 05:46:10.458
2025-07-02 05:46:10.463 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:10.469 # identical
2025-07-02 05:46:10.473
2025-07-02 05:46:10.478 # pump out diffs from before the synch point
2025-07-02 05:46:10.483 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:10.487
2025-07-02 05:46:10.493 # do intraline marking on the synch pair
2025-07-02 05:46:10.501 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:10.511 if eqi is None:
2025-07-02 05:46:10.520 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:10.526 atags = btags = ""
2025-07-02 05:46:10.532 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:10.537 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:10.542 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:10.547 if tag == 'replace':
2025-07-02 05:46:10.551 atags += '^' * la
2025-07-02 05:46:10.557 btags += '^' * lb
2025-07-02 05:46:10.566 elif tag == 'delete':
2025-07-02 05:46:10.573 atags += '-' * la
2025-07-02 05:46:10.580 elif tag == 'insert':
2025-07-02 05:46:10.587 btags += '+' * lb
2025-07-02 05:46:10.592 elif tag == 'equal':
2025-07-02 05:46:10.596 atags += ' ' * la
2025-07-02 05:46:10.601 btags += ' ' * lb
2025-07-02 05:46:10.606 else:
2025-07-02 05:46:10.611 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:10.616 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:10.621 else:
2025-07-02 05:46:10.627 # the synch pair is identical
2025-07-02 05:46:10.631 yield '  ' + aelt
2025-07-02 05:46:10.639
2025-07-02 05:46:10.647 # pump out diffs from after the synch point
2025-07-02 05:46:10.656 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:10.665
2025-07-02 05:46:10.677 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:10.685 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:10.691
2025-07-02 05:46:10.698 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:10.704 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:10.711 alo = 48, ahi = 1101
2025-07-02 05:46:10.718 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:10.726 blo = 48, bhi = 1101
2025-07-02 05:46:10.734
2025-07-02 05:46:10.746 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:10.758 g = []
2025-07-02 05:46:10.769 if alo < ahi:
2025-07-02 05:46:10.777 if blo < bhi:
2025-07-02 05:46:10.785 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:10.792 else:
2025-07-02 05:46:10.799 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:10.805 elif blo < bhi:
2025-07-02 05:46:10.812 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:10.819
2025-07-02 05:46:10.829 >       yield from g
2025-07-02 05:46:10.840
2025-07-02 05:46:10.848 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:10.858 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:10.869
2025-07-02 05:46:10.877 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:10.885 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:10.890 alo = 48, ahi = 1101
2025-07-02 05:46:10.896 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:10.901 blo = 48, bhi = 1101
2025-07-02 05:46:10.906
2025-07-02 05:46:10.912 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:10.918 r"""
2025-07-02 05:46:10.925 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:10.932 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:10.941 synch point, and intraline difference marking is done on the
2025-07-02 05:46:10.948 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:10.956
2025-07-02 05:46:10.963 Example:
2025-07-02 05:46:10.971
2025-07-02 05:46:10.980 >>> d = Differ()
2025-07-02 05:46:10.990 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:10.998 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:11.008 >>> print(''.join(results), end="")
2025-07-02 05:46:11.018 - abcDefghiJkl
2025-07-02 05:46:11.033 + abcdefGhijkl
2025-07-02 05:46:11.047 """
2025-07-02 05:46:11.053
2025-07-02 05:46:11.059 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:11.067 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:11.074 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:11.085 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:11.094 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:11.101
2025-07-02 05:46:11.107 # search for the pair that matches best without being identical
2025-07-02 05:46:11.114 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:11.121 # on junk -- unless we have to)
2025-07-02 05:46:11.126 for j in range(blo, bhi):
2025-07-02 05:46:11.132 bj = b[j]
2025-07-02 05:46:11.137 cruncher.set_seq2(bj)
2025-07-02 05:46:11.149 for i in range(alo, ahi):
2025-07-02 05:46:11.158 ai = a[i]
2025-07-02 05:46:11.166 if ai == bj:
2025-07-02 05:46:11.176 if eqi is None:
2025-07-02 05:46:11.185 eqi, eqj = i, j
2025-07-02 05:46:11.193 continue
2025-07-02 05:46:11.201 cruncher.set_seq1(ai)
2025-07-02 05:46:11.208 # computing similarity is expensive, so use the quick
2025-07-02 05:46:11.215 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:11.220 # compares by a factor of 3.
2025-07-02 05:46:11.226 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:11.232 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:11.240 # of the computation is cached by cruncher
2025-07-02 05:46:11.247 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:11.255 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:11.262 cruncher.ratio() > best_ratio:
2025-07-02 05:46:11.271 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:11.284 if best_ratio < cutoff:
2025-07-02 05:46:11.294 # no non-identical "pretty close" pair
2025-07-02 05:46:11.303 if eqi is None:
2025-07-02 05:46:11.312 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:11.319 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:11.325 return
2025-07-02 05:46:11.337 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:11.346 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:11.354 else:
2025-07-02 05:46:11.367 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:11.378 eqi = None
2025-07-02 05:46:11.390
2025-07-02 05:46:11.401 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:11.409 # identical
2025-07-02 05:46:11.416
2025-07-02 05:46:11.423 # pump out diffs from before the synch point
2025-07-02 05:46:11.430 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:11.436
2025-07-02 05:46:11.442 # do intraline marking on the synch pair
2025-07-02 05:46:11.453 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:11.465 if eqi is None:
2025-07-02 05:46:11.473 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:11.485 atags = btags = ""
2025-07-02 05:46:11.495 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:11.503 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:11.511 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:11.518 if tag == 'replace':
2025-07-02 05:46:11.525 atags += '^' * la
2025-07-02 05:46:11.531 btags += '^' * lb
2025-07-02 05:46:11.539 elif tag == 'delete':
2025-07-02 05:46:11.549 atags += '-' * la
2025-07-02 05:46:11.560 elif tag == 'insert':
2025-07-02 05:46:11.570 btags += '+' * lb
2025-07-02 05:46:11.577 elif tag == 'equal':
2025-07-02 05:46:11.584 atags += ' ' * la
2025-07-02 05:46:11.591 btags += ' ' * lb
2025-07-02 05:46:11.597 else:
2025-07-02 05:46:11.603 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:11.608 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:11.615 else:
2025-07-02 05:46:11.625 # the synch pair is identical
2025-07-02 05:46:11.635 yield '  ' + aelt
2025-07-02 05:46:11.644
2025-07-02 05:46:11.655 # pump out diffs from after the synch point
2025-07-02 05:46:11.663 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:11.673
2025-07-02 05:46:11.686 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:11.698 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:11.707
2025-07-02 05:46:11.716 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:11.724 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:11.734 alo = 49, ahi = 1101
2025-07-02 05:46:11.745 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:11.758 blo = 49, bhi = 1101
2025-07-02 05:46:11.769
2025-07-02 05:46:11.782 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:11.792 g = []
2025-07-02 05:46:11.800 if alo < ahi:
2025-07-02 05:46:11.807 if blo < bhi:
2025-07-02 05:46:11.816 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:11.826 else:
2025-07-02 05:46:11.835 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:11.845 elif blo < bhi:
2025-07-02 05:46:11.853 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:11.860
2025-07-02 05:46:11.866 >       yield from g
2025-07-02 05:46:11.871
2025-07-02 05:46:11.876 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:11.882 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:11.892
2025-07-02 05:46:11.901 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:11.910 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:11.916 alo = 49, ahi = 1101
2025-07-02 05:46:11.922 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:11.932 blo = 49, bhi = 1101
2025-07-02 05:46:11.938
2025-07-02 05:46:11.944 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:11.950 r"""
2025-07-02 05:46:11.962 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:11.971 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:11.980 synch point, and intraline difference marking is done on the
2025-07-02 05:46:11.986 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:11.993
2025-07-02 05:46:11.999 Example:
2025-07-02 05:46:12.005
2025-07-02 05:46:12.012 >>> d = Differ()
2025-07-02 05:46:12.019 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:12.026 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:12.039 >>> print(''.join(results), end="")
2025-07-02 05:46:12.049 - abcDefghiJkl
2025-07-02 05:46:12.069 + abcdefGhijkl
2025-07-02 05:46:12.085 """
2025-07-02 05:46:12.092
2025-07-02 05:46:12.100 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:12.108 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:12.116 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:12.123 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:12.130 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:12.139
2025-07-02 05:46:12.150 # search for the pair that matches best without being identical
2025-07-02 05:46:12.159 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:12.167 # on junk -- unless we have to)
2025-07-02 05:46:12.180 for j in range(blo, bhi):
2025-07-02 05:46:12.192 bj = b[j]
2025-07-02 05:46:12.203 cruncher.set_seq2(bj)
2025-07-02 05:46:12.210 for i in range(alo, ahi):
2025-07-02 05:46:12.217 ai = a[i]
2025-07-02 05:46:12.224 if ai == bj:
2025-07-02 05:46:12.231 if eqi is None:
2025-07-02 05:46:12.243 eqi, eqj = i, j
2025-07-02 05:46:12.255 continue
2025-07-02 05:46:12.264 cruncher.set_seq1(ai)
2025-07-02 05:46:12.273 # computing similarity is expensive, so use the quick
2025-07-02 05:46:12.281 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:12.287 # compares by a factor of 3.
2025-07-02 05:46:12.292 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:12.297 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:12.302 # of the computation is cached by cruncher
2025-07-02 05:46:12.307 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:12.314 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:12.318 cruncher.ratio() > best_ratio:
2025-07-02 05:46:12.324 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:12.331 if best_ratio < cutoff:
2025-07-02 05:46:12.337 # no non-identical "pretty close" pair
2025-07-02 05:46:12.343 if eqi is None:
2025-07-02 05:46:12.351 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:12.363 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:12.373 return
2025-07-02 05:46:12.381 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:12.386 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:12.393 else:
2025-07-02 05:46:12.402 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:12.411 eqi = None
2025-07-02 05:46:12.424
2025-07-02 05:46:12.434 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:12.444 # identical
2025-07-02 05:46:12.452
2025-07-02 05:46:12.460 # pump out diffs from before the synch point
2025-07-02 05:46:12.466 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:12.475
2025-07-02 05:46:12.486 # do intraline marking on the synch pair
2025-07-02 05:46:12.494 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:12.500 if eqi is None:
2025-07-02 05:46:12.506 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:12.510 atags = btags = ""
2025-07-02 05:46:12.517 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:12.522 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:12.528 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:12.534 if tag == 'replace':
2025-07-02 05:46:12.541 atags += '^' * la
2025-07-02 05:46:12.547 btags += '^' * lb
2025-07-02 05:46:12.555 elif tag == 'delete':
2025-07-02 05:46:12.565 atags += '-' * la
2025-07-02 05:46:12.573 elif tag == 'insert':
2025-07-02 05:46:12.579 btags += '+' * lb
2025-07-02 05:46:12.588 elif tag == 'equal':
2025-07-02 05:46:12.597 atags += ' ' * la
2025-07-02 05:46:12.604 btags += ' ' * lb
2025-07-02 05:46:12.610 else:
2025-07-02 05:46:12.614 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:12.626 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:12.636 else:
2025-07-02 05:46:12.644 # the synch pair is identical
2025-07-02 05:46:12.651 yield '  ' + aelt
2025-07-02 05:46:12.658
2025-07-02 05:46:12.664 # pump out diffs from after the synch point
2025-07-02 05:46:12.669 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:12.675
2025-07-02 05:46:12.680 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:12.686 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:12.691
2025-07-02 05:46:12.697 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:12.707 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:12.719 alo = 50, ahi = 1101
2025-07-02 05:46:12.731 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:12.740 blo = 50, bhi = 1101
2025-07-02 05:46:12.748
2025-07-02 05:46:12.755 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:12.763 g = []
2025-07-02 05:46:12.776 if alo < ahi:
2025-07-02 05:46:12.784 if blo < bhi:
2025-07-02 05:46:12.792 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:12.799 else:
2025-07-02 05:46:12.807 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:12.817 elif blo < bhi:
2025-07-02 05:46:12.825 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:12.831
2025-07-02 05:46:12.838 >       yield from g
2025-07-02 05:46:12.844
2025-07-02 05:46:12.850 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:12.857 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:12.862
2025-07-02 05:46:12.873 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:12.883 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:12.891 alo = 50, ahi = 1101
2025-07-02 05:46:12.898 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:12.909 blo = 50, bhi = 1101
2025-07-02 05:46:12.920
2025-07-02 05:46:12.928 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:12.949 r"""
2025-07-02 05:46:12.957 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:12.964 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:12.974 synch point, and intraline difference marking is done on the
2025-07-02 05:46:12.981 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:12.987
2025-07-02 05:46:12.994 Example:
2025-07-02 05:46:13.002
2025-07-02 05:46:13.012 >>> d = Differ()
2025-07-02 05:46:13.019 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:13.025 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:13.030 >>> print(''.join(results), end="")
2025-07-02 05:46:13.035 - abcDefghiJkl
2025-07-02 05:46:13.044 + abcdefGhijkl
2025-07-02 05:46:13.054 """
2025-07-02 05:46:13.059
2025-07-02 05:46:13.063 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:13.068 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:13.073 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:13.078 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:13.083 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:13.088
2025-07-02 05:46:13.094 # search for the pair that matches best without being identical
2025-07-02 05:46:13.100 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:13.107 # on junk -- unless we have to)
2025-07-02 05:46:13.115 for j in range(blo, bhi):
2025-07-02 05:46:13.123 bj = b[j]
2025-07-02 05:46:13.129 cruncher.set_seq2(bj)
2025-07-02 05:46:13.135 for i in range(alo, ahi):
2025-07-02 05:46:13.142 ai = a[i]
2025-07-02 05:46:13.149 if ai == bj:
2025-07-02 05:46:13.155 if eqi is None:
2025-07-02 05:46:13.162 eqi, eqj = i, j
2025-07-02 05:46:13.169 continue
2025-07-02 05:46:13.175 cruncher.set_seq1(ai)
2025-07-02 05:46:13.182 # computing similarity is expensive, so use the quick
2025-07-02 05:46:13.189 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:13.195 # compares by a factor of 3.
2025-07-02 05:46:13.202 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:13.209 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:13.216 # of the computation is cached by cruncher
2025-07-02 05:46:13.222 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:13.229 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:13.236 cruncher.ratio() > best_ratio:
2025-07-02 05:46:13.243 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:13.249 if best_ratio < cutoff:
2025-07-02 05:46:13.256 # no non-identical "pretty close" pair
2025-07-02 05:46:13.262 if eqi is None:
2025-07-02 05:46:13.269 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:13.276 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:13.282 return
2025-07-02 05:46:13.291 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:13.303 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:13.310 else:
2025-07-02 05:46:13.317 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:13.328 eqi = None
2025-07-02 05:46:13.338
2025-07-02 05:46:13.345 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:13.355 # identical
2025-07-02 05:46:13.362
2025-07-02 05:46:13.369 # pump out diffs from before the synch point
2025-07-02 05:46:13.375 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:13.381
2025-07-02 05:46:13.388 # do intraline marking on the synch pair
2025-07-02 05:46:13.394 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:13.404 if eqi is None:
2025-07-02 05:46:13.414 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:13.422 atags = btags = ""
2025-07-02 05:46:13.428 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:13.434 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:13.439 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:13.444 if tag == 'replace':
2025-07-02 05:46:13.450 atags += '^' * la
2025-07-02 05:46:13.456 btags += '^' * lb
2025-07-02 05:46:13.463 elif tag == 'delete':
2025-07-02 05:46:13.469 atags += '-' * la
2025-07-02 05:46:13.474 elif tag == 'insert':
2025-07-02 05:46:13.484 btags += '+' * lb
2025-07-02 05:46:13.496 elif tag == 'equal':
2025-07-02 05:46:13.504 atags += ' ' * la
2025-07-02 05:46:13.511 btags += ' ' * lb
2025-07-02 05:46:13.520 else:
2025-07-02 05:46:13.532 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:13.539 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:13.546 else:
2025-07-02 05:46:13.553 # the synch pair is identical
2025-07-02 05:46:13.559 yield '  ' + aelt
2025-07-02 05:46:13.567
2025-07-02 05:46:13.577 # pump out diffs from after the synch point
2025-07-02 05:46:13.587 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:13.597
2025-07-02 05:46:13.608 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:13.619 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:13.631
2025-07-02 05:46:13.642 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:13.652 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:13.659 alo = 51, ahi = 1101
2025-07-02 05:46:13.666 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:13.671 blo = 51, bhi = 1101
2025-07-02 05:46:13.677
2025-07-02 05:46:13.684 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:13.690 g = []
2025-07-02 05:46:13.702 if alo < ahi:
2025-07-02 05:46:13.712 if blo < bhi:
2025-07-02 05:46:13.721 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:13.728 else:
2025-07-02 05:46:13.735 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:13.742 elif blo < bhi:
2025-07-02 05:46:13.751 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:13.757
2025-07-02 05:46:13.765 >       yield from g
2025-07-02 05:46:13.771
2025-07-02 05:46:13.778 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:13.787 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:13.795
2025-07-02 05:46:13.803 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:13.811 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:13.819 alo = 51, ahi = 1101
2025-07-02 05:46:13.831 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:13.839 blo = 51, bhi = 1101
2025-07-02 05:46:13.849
2025-07-02 05:46:13.861 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:13.871 r"""
2025-07-02 05:46:13.880 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:13.888 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:13.895 synch point, and intraline difference marking is done on the
2025-07-02 05:46:13.902 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:13.915
2025-07-02 05:46:13.927 Example:
2025-07-02 05:46:13.936
2025-07-02 05:46:13.943 >>> d = Differ()
2025-07-02 05:46:13.952 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:13.963 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:13.973 >>> print(''.join(results), end="")
2025-07-02 05:46:13.980 - abcDefghiJkl
2025-07-02 05:46:14.004 + abcdefGhijkl
2025-07-02 05:46:14.023 """
2025-07-02 05:46:14.038
2025-07-02 05:46:14.048 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:14.056 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:14.063 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:14.070 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:14.081 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:14.088
2025-07-02 05:46:14.094 # search for the pair that matches best without being identical
2025-07-02 05:46:14.100 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:14.106 # on junk -- unless we have to)
2025-07-02 05:46:14.113 for j in range(blo, bhi):
2025-07-02 05:46:14.118 bj = b[j]
2025-07-02 05:46:14.126 cruncher.set_seq2(bj)
2025-07-02 05:46:14.131 for i in range(alo, ahi):
2025-07-02 05:46:14.137 ai = a[i]
2025-07-02 05:46:14.141 if ai == bj:
2025-07-02 05:46:14.147 if eqi is None:
2025-07-02 05:46:14.152 eqi, eqj = i, j
2025-07-02 05:46:14.158 continue
2025-07-02 05:46:14.164 cruncher.set_seq1(ai)
2025-07-02 05:46:14.170 # computing similarity is expensive, so use the quick
2025-07-02 05:46:14.176 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:14.183 # compares by a factor of 3.
2025-07-02 05:46:14.189 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:14.203 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:14.213 # of the computation is cached by cruncher
2025-07-02 05:46:14.221 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:14.227 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:14.236 cruncher.ratio() > best_ratio:
2025-07-02 05:46:14.247 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:14.255 if best_ratio < cutoff:
2025-07-02 05:46:14.266 # no non-identical "pretty close" pair
2025-07-02 05:46:14.278 if eqi is None:
2025-07-02 05:46:14.290 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:14.300 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:14.307 return
2025-07-02 05:46:14.315 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:14.326 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:14.341 else:
2025-07-02 05:46:14.353 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:14.366 eqi = None
2025-07-02 05:46:14.376
2025-07-02 05:46:14.388 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:14.399 # identical
2025-07-02 05:46:14.408
2025-07-02 05:46:14.415 # pump out diffs from before the synch point
2025-07-02 05:46:14.423 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:14.429
2025-07-02 05:46:14.435 # do intraline marking on the synch pair
2025-07-02 05:46:14.442 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:14.453 if eqi is None:
2025-07-02 05:46:14.462 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:14.472 atags = btags = ""
2025-07-02 05:46:14.483 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:14.493 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:14.500 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:14.508 if tag == 'replace':
2025-07-02 05:46:14.514 atags += '^' * la
2025-07-02 05:46:14.524 btags += '^' * lb
2025-07-02 05:46:14.534 elif tag == 'delete':
2025-07-02 05:46:14.542 atags += '-' * la
2025-07-02 05:46:14.552 elif tag == 'insert':
2025-07-02 05:46:14.561 btags += '+' * lb
2025-07-02 05:46:14.570 elif tag == 'equal':
2025-07-02 05:46:14.581 atags += ' ' * la
2025-07-02 05:46:14.591 btags += ' ' * lb
2025-07-02 05:46:14.601 else:
2025-07-02 05:46:14.609 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:14.615 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:14.622 else:
2025-07-02 05:46:14.633 # the synch pair is identical
2025-07-02 05:46:14.646 yield '  ' + aelt
2025-07-02 05:46:14.659
2025-07-02 05:46:14.673 # pump out diffs from after the synch point
2025-07-02 05:46:14.685 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:14.696
2025-07-02 05:46:14.704 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:14.713 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:14.719
2025-07-02 05:46:14.727 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:14.739 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:14.750 alo = 52, ahi = 1101
2025-07-02 05:46:14.758 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:14.765 blo = 52, bhi = 1101
2025-07-02 05:46:14.770
2025-07-02 05:46:14.777 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:14.787 g = []
2025-07-02 05:46:14.798 if alo < ahi:
2025-07-02 05:46:14.806 if blo < bhi:
2025-07-02 05:46:14.814 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:14.822 else:
2025-07-02 05:46:14.830 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:14.838 elif blo < bhi:
2025-07-02 05:46:14.849 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:14.860
2025-07-02 05:46:14.867 >       yield from g
2025-07-02 05:46:14.874
2025-07-02 05:46:14.881 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:14.892 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:14.900
2025-07-02 05:46:14.909 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:14.921 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:14.930 alo = 52, ahi = 1101
2025-07-02 05:46:14.941 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:14.954 blo = 52, bhi = 1101
2025-07-02 05:46:14.965
2025-07-02 05:46:14.978 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:14.989 r"""
2025-07-02 05:46:14.997 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:15.005 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:15.011 synch point, and intraline difference marking is done on the
2025-07-02 05:46:15.019 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:15.030
2025-07-02 05:46:15.037 Example:
2025-07-02 05:46:15.045
2025-07-02 05:46:15.053 >>> d = Differ()
2025-07-02 05:46:15.061 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:15.067 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:15.074 >>> print(''.join(results), end="")
2025-07-02 05:46:15.081 - abcDefghiJkl
2025-07-02 05:46:15.093 + abcdefGhijkl
2025-07-02 05:46:15.107 """
2025-07-02 05:46:15.123
2025-07-02 05:46:15.134 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:15.142 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:15.149 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:15.155 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:15.161 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:15.171
2025-07-02 05:46:15.180 # search for the pair that matches best without being identical
2025-07-02 05:46:15.189 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:15.196 # on junk -- unless we have to)
2025-07-02 05:46:15.203 for j in range(blo, bhi):
2025-07-02 05:46:15.209 bj = b[j]
2025-07-02 05:46:15.216 cruncher.set_seq2(bj)
2025-07-02 05:46:15.222 for i in range(alo, ahi):
2025-07-02 05:46:15.228 ai = a[i]
2025-07-02 05:46:15.234 if ai == bj:
2025-07-02 05:46:15.245 if eqi is None:
2025-07-02 05:46:15.256 eqi, eqj = i, j
2025-07-02 05:46:15.265 continue
2025-07-02 05:46:15.272 cruncher.set_seq1(ai)
2025-07-02 05:46:15.279 # computing similarity is expensive, so use the quick
2025-07-02 05:46:15.287 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:15.300 # compares by a factor of 3.
2025-07-02 05:46:15.310 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:15.318 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:15.325 # of the computation is cached by cruncher
2025-07-02 05:46:15.338 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:15.349 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:15.360 cruncher.ratio() > best_ratio:
2025-07-02 05:46:15.373 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:15.383 if best_ratio < cutoff:
2025-07-02 05:46:15.391 # no non-identical "pretty close" pair
2025-07-02 05:46:15.401 if eqi is None:
2025-07-02 05:46:15.411 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:15.418 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:15.425 return
2025-07-02 05:46:15.433 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:15.439 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:15.444 else:
2025-07-02 05:46:15.451 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:15.461 eqi = None
2025-07-02 05:46:15.471
2025-07-02 05:46:15.478 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:15.489 # identical
2025-07-02 05:46:15.503
2025-07-02 05:46:15.514 # pump out diffs from before the synch point
2025-07-02 05:46:15.526 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:15.535
2025-07-02 05:46:15.544 # do intraline marking on the synch pair
2025-07-02 05:46:15.551 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:15.557 if eqi is None:
2025-07-02 05:46:15.568 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:15.580 atags = btags = ""
2025-07-02 05:46:15.588 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:15.595 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:15.602 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:15.610 if tag == 'replace':
2025-07-02 05:46:15.617 atags += '^' * la
2025-07-02 05:46:15.627 btags += '^' * lb
2025-07-02 05:46:15.635 elif tag == 'delete':
2025-07-02 05:46:15.642 atags += '-' * la
2025-07-02 05:46:15.648 elif tag == 'insert':
2025-07-02 05:46:15.661 btags += '+' * lb
2025-07-02 05:46:15.673 elif tag == 'equal':
2025-07-02 05:46:15.684 atags += ' ' * la
2025-07-02 05:46:15.696 btags += ' ' * lb
2025-07-02 05:46:15.707 else:
2025-07-02 05:46:15.719 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:15.730 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:15.739 else:
2025-07-02 05:46:15.746 # the synch pair is identical
2025-07-02 05:46:15.757 yield '  ' + aelt
2025-07-02 05:46:15.769
2025-07-02 05:46:15.782 # pump out diffs from after the synch point
2025-07-02 05:46:15.792 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:15.806
2025-07-02 05:46:15.821 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:15.830 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:15.838
2025-07-02 05:46:15.845 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:15.853 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:15.859 alo = 53, ahi = 1101
2025-07-02 05:46:15.866 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:15.873 blo = 53, bhi = 1101
2025-07-02 05:46:15.880
2025-07-02 05:46:15.887 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:15.895 g = []
2025-07-02 05:46:15.903 if alo < ahi:
2025-07-02 05:46:15.909 if blo < bhi:
2025-07-02 05:46:15.917 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:15.928 else:
2025-07-02 05:46:15.941 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:15.954 elif blo < bhi:
2025-07-02 05:46:15.963 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:15.969
2025-07-02 05:46:15.975 >       yield from g
2025-07-02 05:46:15.981
2025-07-02 05:46:15.985 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:15.996 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:16.007
2025-07-02 05:46:16.015 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:16.023 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:16.029 alo = 53, ahi = 1101
2025-07-02 05:46:16.041 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:16.051 blo = 53, bhi = 1101
2025-07-02 05:46:16.063
2025-07-02 05:46:16.072 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:16.085 r"""
2025-07-02 05:46:16.094 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:16.103 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:16.117 synch point, and intraline difference marking is done on the
2025-07-02 05:46:16.129 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:16.142
2025-07-02 05:46:16.153 Example:
2025-07-02 05:46:16.166
2025-07-02 05:46:16.179 >>> d = Differ()
2025-07-02 05:46:16.192 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:16.205 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:16.216 >>> print(''.join(results), end="")
2025-07-02 05:46:16.229 - abcDefghiJkl
2025-07-02 05:46:16.251 + abcdefGhijkl
2025-07-02 05:46:16.271 """
2025-07-02 05:46:16.284
2025-07-02 05:46:16.294 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:16.306 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:16.316 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:16.325 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:16.332 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:16.340
2025-07-02 05:46:16.346 # search for the pair that matches best without being identical
2025-07-02 05:46:16.354 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:16.362 # on junk -- unless we have to)
2025-07-02 05:46:16.371 for j in range(blo, bhi):
2025-07-02 05:46:16.380 bj = b[j]
2025-07-02 05:46:16.392 cruncher.set_seq2(bj)
2025-07-02 05:46:16.402 for i in range(alo, ahi):
2025-07-02 05:46:16.409 ai = a[i]
2025-07-02 05:46:16.416 if ai == bj:
2025-07-02 05:46:16.423 if eqi is None:
2025-07-02 05:46:16.430 eqi, eqj = i, j
2025-07-02 05:46:16.436 continue
2025-07-02 05:46:16.443 cruncher.set_seq1(ai)
2025-07-02 05:46:16.456 # computing similarity is expensive, so use the quick
2025-07-02 05:46:16.467 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:16.476 # compares by a factor of 3.
2025-07-02 05:46:16.483 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:16.493 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:16.505 # of the computation is cached by cruncher
2025-07-02 05:46:16.514 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:16.522 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:16.533 cruncher.ratio() > best_ratio:
2025-07-02 05:46:16.544 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:16.552 if best_ratio < cutoff:
2025-07-02 05:46:16.558 # no non-identical "pretty close" pair
2025-07-02 05:46:16.565 if eqi is None:
2025-07-02 05:46:16.570 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:16.576 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:16.582 return
2025-07-02 05:46:16.591 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:16.598 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:16.604 else:
2025-07-02 05:46:16.609 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:16.615 eqi = None
2025-07-02 05:46:16.622
2025-07-02 05:46:16.630 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:16.636 # identical
2025-07-02 05:46:16.644
2025-07-02 05:46:16.650 # pump out diffs from before the synch point
2025-07-02 05:46:16.656 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:16.662
2025-07-02 05:46:16.670 # do intraline marking on the synch pair
2025-07-02 05:46:16.681 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:16.690 if eqi is None:
2025-07-02 05:46:16.700 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:16.713 atags = btags = ""
2025-07-02 05:46:16.724 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:16.733 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:16.746 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:16.756 if tag == 'replace':
2025-07-02 05:46:16.769 atags += '^' * la
2025-07-02 05:46:16.779 btags += '^' * lb
2025-07-02 05:46:16.786 elif tag == 'delete':
2025-07-02 05:46:16.792 atags += '-' * la
2025-07-02 05:46:16.797 elif tag == 'insert':
2025-07-02 05:46:16.802 btags += '+' * lb
2025-07-02 05:46:16.806 elif tag == 'equal':
2025-07-02 05:46:16.811 atags += ' ' * la
2025-07-02 05:46:16.815 btags += ' ' * lb
2025-07-02 05:46:16.820 else:
2025-07-02 05:46:16.834 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:16.843 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:16.851 else:
2025-07-02 05:46:16.863 # the synch pair is identical
2025-07-02 05:46:16.875 yield '  ' + aelt
2025-07-02 05:46:16.885
2025-07-02 05:46:16.893 # pump out diffs from after the synch point
2025-07-02 05:46:16.898 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:16.903
2025-07-02 05:46:16.909 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:16.914 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:16.920
2025-07-02 05:46:16.926 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:16.932 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:16.938 alo = 54, ahi = 1101
2025-07-02 05:46:16.945 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:16.951 blo = 54, bhi = 1101
2025-07-02 05:46:16.958
2025-07-02 05:46:16.968 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:16.978 g = []
2025-07-02 05:46:16.987 if alo < ahi:
2025-07-02 05:46:16.999 if blo < bhi:
2025-07-02 05:46:17.009 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:17.015 else:
2025-07-02 05:46:17.022 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:17.031 elif blo < bhi:
2025-07-02 05:46:17.043 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:17.051
2025-07-02 05:46:17.059 >       yield from g
2025-07-02 05:46:17.066
2025-07-02 05:46:17.072 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:17.078 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:17.083
2025-07-02 05:46:17.089 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:17.097 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:17.103 alo = 54, ahi = 1101
2025-07-02 05:46:17.111 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:17.118 blo = 54, bhi = 1101
2025-07-02 05:46:17.123
2025-07-02 05:46:17.129 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:17.135 r"""
2025-07-02 05:46:17.141 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:17.147 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:17.152 synch point, and intraline difference marking is done on the
2025-07-02 05:46:17.157 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:17.162
2025-07-02 05:46:17.167 Example:
2025-07-02 05:46:17.173
2025-07-02 05:46:17.179 >>> d = Differ()
2025-07-02 05:46:17.185 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:17.197 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:17.207 >>> print(''.join(results), end="")
2025-07-02 05:46:17.213 - abcDefghiJkl
2025-07-02 05:46:17.225 + abcdefGhijkl
2025-07-02 05:46:17.238 """
2025-07-02 05:46:17.245
2025-07-02 05:46:17.251 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:17.260 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:17.272 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:17.284 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:17.296 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:17.305
2025-07-02 05:46:17.312 # search for the pair that matches best without being identical
2025-07-02 05:46:17.319 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:17.325 # on junk -- unless we have to)
2025-07-02 05:46:17.330 for j in range(blo, bhi):
2025-07-02 05:46:17.337 bj = b[j]
2025-07-02 05:46:17.344 cruncher.set_seq2(bj)
2025-07-02 05:46:17.350 for i in range(alo, ahi):
2025-07-02 05:46:17.356 ai = a[i]
2025-07-02 05:46:17.363 if ai == bj:
2025-07-02 05:46:17.377 if eqi is None:
2025-07-02 05:46:17.387 eqi, eqj = i, j
2025-07-02 05:46:17.401 continue
2025-07-02 05:46:17.412 cruncher.set_seq1(ai)
2025-07-02 05:46:17.421 # computing similarity is expensive, so use the quick
2025-07-02 05:46:17.429 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:17.436 # compares by a factor of 3.
2025-07-02 05:46:17.453 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:17.467 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:17.476 # of the computation is cached by cruncher
2025-07-02 05:46:17.484 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:17.491 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:17.499 cruncher.ratio() > best_ratio:
2025-07-02 05:46:17.506 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:17.512 if best_ratio < cutoff:
2025-07-02 05:46:17.518 # no non-identical "pretty close" pair
2025-07-02 05:46:17.524 if eqi is None:
2025-07-02 05:46:17.531 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:17.545 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:17.558 return
2025-07-02 05:46:17.571 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:17.582 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:17.592 else:
2025-07-02 05:46:17.603 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:17.612 eqi = None
2025-07-02 05:46:17.619
2025-07-02 05:46:17.626 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:17.638 # identical
2025-07-02 05:46:17.650
2025-07-02 05:46:17.658 # pump out diffs from before the synch point
2025-07-02 05:46:17.671 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:17.681
2025-07-02 05:46:17.690 # do intraline marking on the synch pair
2025-07-02 05:46:17.699 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:17.711 if eqi is None:
2025-07-02 05:46:17.721 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:17.729 atags = btags = ""
2025-07-02 05:46:17.741 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:17.750 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:17.758 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:17.767 if tag == 'replace':
2025-07-02 05:46:17.777 atags += '^' * la
2025-07-02 05:46:17.787 btags += '^' * lb
2025-07-02 05:46:17.795 elif tag == 'delete':
2025-07-02 05:46:17.803 atags += '-' * la
2025-07-02 05:46:17.809 elif tag == 'insert':
2025-07-02 05:46:17.814 btags += '+' * lb
2025-07-02 05:46:17.818 elif tag == 'equal':
2025-07-02 05:46:17.829 atags += ' ' * la
2025-07-02 05:46:17.838 btags += ' ' * lb
2025-07-02 05:46:17.846 else:
2025-07-02 05:46:17.856 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:17.867 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:17.876 else:
2025-07-02 05:46:17.884 # the synch pair is identical
2025-07-02 05:46:17.890 yield '  ' + aelt
2025-07-02 05:46:17.897
2025-07-02 05:46:17.903 # pump out diffs from after the synch point
2025-07-02 05:46:17.910 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:17.916
2025-07-02 05:46:17.922 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:17.931 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:17.942
2025-07-02 05:46:17.951 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:17.959 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:17.966 alo = 55, ahi = 1101
2025-07-02 05:46:17.979 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:17.989 blo = 55, bhi = 1101
2025-07-02 05:46:18.000
2025-07-02 05:46:18.011 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:18.019 g = []
2025-07-02 05:46:18.026 if alo < ahi:
2025-07-02 05:46:18.037 if blo < bhi:
2025-07-02 05:46:18.048 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:18.061 else:
2025-07-02 05:46:18.072 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:18.083 elif blo < bhi:
2025-07-02 05:46:18.093 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:18.100
2025-07-02 05:46:18.107 >       yield from g
2025-07-02 05:46:18.111
2025-07-02 05:46:18.116 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:18.122 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:18.132
2025-07-02 05:46:18.145 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:18.158 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:18.170 alo = 55, ahi = 1101
2025-07-02 05:46:18.176 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:18.183 blo = 55, bhi = 1101
2025-07-02 05:46:18.194
2025-07-02 05:46:18.202 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:18.210 r"""
2025-07-02 05:46:18.220 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:18.233 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:18.243 synch point, and intraline difference marking is done on the
2025-07-02 05:46:18.252 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:18.259
2025-07-02 05:46:18.267 Example:
2025-07-02 05:46:18.276
2025-07-02 05:46:18.282 >>> d = Differ()
2025-07-02 05:46:18.289 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:18.296 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:18.302 >>> print(''.join(results), end="")
2025-07-02 05:46:18.308 - abcDefghiJkl
2025-07-02 05:46:18.321 + abcdefGhijkl
2025-07-02 05:46:18.337 """
2025-07-02 05:46:18.344
2025-07-02 05:46:18.352 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:18.359 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:18.366 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:18.373 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:18.383 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:18.390
2025-07-02 05:46:18.402 # search for the pair that matches best without being identical
2025-07-02 05:46:18.412 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:18.420 # on junk -- unless we have to)
2025-07-02 05:46:18.427 for j in range(blo, bhi):
2025-07-02 05:46:18.435 bj = b[j]
2025-07-02 05:46:18.445 cruncher.set_seq2(bj)
2025-07-02 05:46:18.460 for i in range(alo, ahi):
2025-07-02 05:46:18.470 ai = a[i]
2025-07-02 05:46:18.482 if ai == bj:
2025-07-02 05:46:18.493 if eqi is None:
2025-07-02 05:46:18.504 eqi, eqj = i, j
2025-07-02 05:46:18.514 continue
2025-07-02 05:46:18.523 cruncher.set_seq1(ai)
2025-07-02 05:46:18.531 # computing similarity is expensive, so use the quick
2025-07-02 05:46:18.537 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:18.550 # compares by a factor of 3.
2025-07-02 05:46:18.562 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:18.570 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:18.578 # of the computation is cached by cruncher
2025-07-02 05:46:18.588 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:18.597 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:18.610 cruncher.ratio() > best_ratio:
2025-07-02 05:46:18.622 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:18.634 if best_ratio < cutoff:
2025-07-02 05:46:18.643 # no non-identical "pretty close" pair
2025-07-02 05:46:18.650 if eqi is None:
2025-07-02 05:46:18.657 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:18.664 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:18.676 return
2025-07-02 05:46:18.686 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:18.698 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:18.707 else:
2025-07-02 05:46:18.714 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:18.727 eqi = None
2025-07-02 05:46:18.741
2025-07-02 05:46:18.750 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:18.758 # identical
2025-07-02 05:46:18.765
2025-07-02 05:46:18.771 # pump out diffs from before the synch point
2025-07-02 05:46:18.776 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:18.781
2025-07-02 05:46:18.787 # do intraline marking on the synch pair
2025-07-02 05:46:18.793 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:18.798 if eqi is None:
2025-07-02 05:46:18.803 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:18.810 atags = btags = ""
2025-07-02 05:46:18.821 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:18.831 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:18.840 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:18.849 if tag == 'replace':
2025-07-02 05:46:18.861 atags += '^' * la
2025-07-02 05:46:18.871 btags += '^' * lb
2025-07-02 05:46:18.880 elif tag == 'delete':
2025-07-02 05:46:18.894 atags += '-' * la
2025-07-02 05:46:18.904 elif tag == 'insert':
2025-07-02 05:46:18.913 btags += '+' * lb
2025-07-02 05:46:18.924 elif tag == 'equal':
2025-07-02 05:46:18.935 atags += ' ' * la
2025-07-02 05:46:18.942 btags += ' ' * lb
2025-07-02 05:46:18.954 else:
2025-07-02 05:46:18.961 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:18.966 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:18.972 else:
2025-07-02 05:46:18.977 # the synch pair is identical
2025-07-02 05:46:18.982 yield '  ' + aelt
2025-07-02 05:46:18.988
2025-07-02 05:46:18.995 # pump out diffs from after the synch point
2025-07-02 05:46:19.003 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:19.011
2025-07-02 05:46:19.021 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:19.030 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:19.038
2025-07-02 05:46:19.048 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:19.057 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:19.064 alo = 56, ahi = 1101
2025-07-02 05:46:19.072 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:19.078 blo = 56, bhi = 1101
2025-07-02 05:46:19.084
2025-07-02 05:46:19.091 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:19.099 g = []
2025-07-02 05:46:19.111 if alo < ahi:
2025-07-02 05:46:19.119 if blo < bhi:
2025-07-02 05:46:19.127 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:19.137 else:
2025-07-02 05:46:19.145 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:19.151 elif blo < bhi:
2025-07-02 05:46:19.157 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:19.162
2025-07-02 05:46:19.167 >       yield from g
2025-07-02 05:46:19.173
2025-07-02 05:46:19.183 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:19.191 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:19.200
2025-07-02 05:46:19.208 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:19.216 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:19.223 alo = 56, ahi = 1101
2025-07-02 05:46:19.231 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:19.237 blo = 56, bhi = 1101
2025-07-02 05:46:19.242
2025-07-02 05:46:19.249 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:19.253 r"""
2025-07-02 05:46:19.258 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:19.263 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:19.268 synch point, and intraline difference marking is done on the
2025-07-02 05:46:19.273 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:19.278
2025-07-02 05:46:19.283 Example:
2025-07-02 05:46:19.289
2025-07-02 05:46:19.295 >>> d = Differ()
2025-07-02 05:46:19.302 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:19.311 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:19.319 >>> print(''.join(results), end="")
2025-07-02 05:46:19.326 - abcDefghiJkl
2025-07-02 05:46:19.344 + abcdefGhijkl
2025-07-02 05:46:19.358 """
2025-07-02 05:46:19.367
2025-07-02 05:46:19.377 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:19.387 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:19.396 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:19.404 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:19.411 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:19.419
2025-07-02 05:46:19.430 # search for the pair that matches best without being identical
2025-07-02 05:46:19.439 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:19.449 # on junk -- unless we have to)
2025-07-02 05:46:19.459 for j in range(blo, bhi):
2025-07-02 05:46:19.470 bj = b[j]
2025-07-02 05:46:19.479 cruncher.set_seq2(bj)
2025-07-02 05:46:19.487 for i in range(alo, ahi):
2025-07-02 05:46:19.495 ai = a[i]
2025-07-02 05:46:19.501 if ai == bj:
2025-07-02 05:46:19.508 if eqi is None:
2025-07-02 05:46:19.514 eqi, eqj = i, j
2025-07-02 05:46:19.526 continue
2025-07-02 05:46:19.540 cruncher.set_seq1(ai)
2025-07-02 05:46:19.550 # computing similarity is expensive, so use the quick
2025-07-02 05:46:19.559 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:19.566 # compares by a factor of 3.
2025-07-02 05:46:19.577 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:19.589 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:19.598 # of the computation is cached by cruncher
2025-07-02 05:46:19.606 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:19.614 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:19.624 cruncher.ratio() > best_ratio:
2025-07-02 05:46:19.632 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:19.639 if best_ratio < cutoff:
2025-07-02 05:46:19.646 # no non-identical "pretty close" pair
2025-07-02 05:46:19.653 if eqi is None:
2025-07-02 05:46:19.659 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:19.666 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:19.672 return
2025-07-02 05:46:19.677 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:19.683 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:19.689 else:
2025-07-02 05:46:19.695 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:19.701 eqi = None
2025-07-02 05:46:19.706
2025-07-02 05:46:19.720 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:19.731 # identical
2025-07-02 05:46:19.742
2025-07-02 05:46:19.754 # pump out diffs from before the synch point
2025-07-02 05:46:19.763 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:19.773
2025-07-02 05:46:19.785 # do intraline marking on the synch pair
2025-07-02 05:46:19.796 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:19.804 if eqi is None:
2025-07-02 05:46:19.812 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:19.818 atags = btags = ""
2025-07-02 05:46:19.825 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:19.831 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:19.839 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:19.849 if tag == 'replace':
2025-07-02 05:46:19.857 atags += '^' * la
2025-07-02 05:46:19.864 btags += '^' * lb
2025-07-02 05:46:19.872 elif tag == 'delete':
2025-07-02 05:46:19.878 atags += '-' * la
2025-07-02 05:46:19.890 elif tag == 'insert':
2025-07-02 05:46:19.902 btags += '+' * lb
2025-07-02 05:46:19.911 elif tag == 'equal':
2025-07-02 05:46:19.925 atags += ' ' * la
2025-07-02 05:46:19.939 btags += ' ' * lb
2025-07-02 05:46:19.948 else:
2025-07-02 05:46:19.956 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:19.969 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:19.979 else:
2025-07-02 05:46:19.992 # the synch pair is identical
2025-07-02 05:46:20.001 yield '  ' + aelt
2025-07-02 05:46:20.008
2025-07-02 05:46:20.015 # pump out diffs from after the synch point
2025-07-02 05:46:20.022 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:20.032
2025-07-02 05:46:20.040 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:20.048 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:20.054
2025-07-02 05:46:20.065 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:20.076 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:20.084 alo = 57, ahi = 1101
2025-07-02 05:46:20.093 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:20.099 blo = 57, bhi = 1101
2025-07-02 05:46:20.105
2025-07-02 05:46:20.111 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:20.117 g = []
2025-07-02 05:46:20.123 if alo < ahi:
2025-07-02 05:46:20.130 if blo < bhi:
2025-07-02 05:46:20.135 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:20.143 else:
2025-07-02 05:46:20.154 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:20.162 elif blo < bhi:
2025-07-02 05:46:20.168 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:20.174
2025-07-02 05:46:20.184 >       yield from g
2025-07-02 05:46:20.193
2025-07-02 05:46:20.201 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:20.208 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:20.215
2025-07-02 05:46:20.226 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:20.235 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:20.243 alo = 57, ahi = 1101
2025-07-02 05:46:20.254 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:20.262 blo = 57, bhi = 1101
2025-07-02 05:46:20.268
2025-07-02 05:46:20.275 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:20.281 r"""
2025-07-02 05:46:20.287 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:20.295 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:20.305 synch point, and intraline difference marking is done on the
2025-07-02 05:46:20.313 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:20.319
2025-07-02 05:46:20.326 Example:
2025-07-02 05:46:20.331
2025-07-02 05:46:20.337 >>> d = Differ()
2025-07-02 05:46:20.350 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:20.361 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:20.375 >>> print(''.join(results), end="")
2025-07-02 05:46:20.388 - abcDefghiJkl
2025-07-02 05:46:20.407 + abcdefGhijkl
2025-07-02 05:46:20.431 """
2025-07-02 05:46:20.441
2025-07-02 05:46:20.451 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:20.464 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:20.473 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:20.481 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:20.493 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:20.504
2025-07-02 05:46:20.512 # search for the pair that matches best without being identical
2025-07-02 05:46:20.520 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:20.527 # on junk -- unless we have to)
2025-07-02 05:46:20.535 for j in range(blo, bhi):
2025-07-02 05:46:20.541 bj = b[j]
2025-07-02 05:46:20.548 cruncher.set_seq2(bj)
2025-07-02 05:46:20.554 for i in range(alo, ahi):
2025-07-02 05:46:20.559 ai = a[i]
2025-07-02 05:46:20.568 if ai == bj:
2025-07-02 05:46:20.579 if eqi is None:
2025-07-02 05:46:20.587 eqi, eqj = i, j
2025-07-02 05:46:20.594 continue
2025-07-02 05:46:20.600 cruncher.set_seq1(ai)
2025-07-02 05:46:20.612 # computing similarity is expensive, so use the quick
2025-07-02 05:46:20.624 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:20.631 # compares by a factor of 3.
2025-07-02 05:46:20.639 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:20.651 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:20.660 # of the computation is cached by cruncher
2025-07-02 05:46:20.668 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:20.675 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:20.683 cruncher.ratio() > best_ratio:
2025-07-02 05:46:20.696 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:20.707 if best_ratio < cutoff:
2025-07-02 05:46:20.718 # no non-identical "pretty close" pair
2025-07-02 05:46:20.728 if eqi is None:
2025-07-02 05:46:20.736 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:20.743 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:20.749 return
2025-07-02 05:46:20.758 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:20.769 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:20.776 else:
2025-07-02 05:46:20.786 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:20.799 eqi = None
2025-07-02 05:46:20.812
2025-07-02 05:46:20.826 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:20.835 # identical
2025-07-02 05:46:20.847
2025-07-02 05:46:20.856 # pump out diffs from before the synch point
2025-07-02 05:46:20.865 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:20.873
2025-07-02 05:46:20.880 # do intraline marking on the synch pair
2025-07-02 05:46:20.886 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:20.891 if eqi is None:
2025-07-02 05:46:20.897 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:20.903 atags = btags = ""
2025-07-02 05:46:20.909 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:20.915 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:20.921 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:20.931 if tag == 'replace':
2025-07-02 05:46:20.936 atags += '^' * la
2025-07-02 05:46:20.943 btags += '^' * lb
2025-07-02 05:46:20.950 elif tag == 'delete':
2025-07-02 05:46:20.958 atags += '-' * la
2025-07-02 05:46:20.965 elif tag == 'insert':
2025-07-02 05:46:20.971 btags += '+' * lb
2025-07-02 05:46:20.977 elif tag == 'equal':
2025-07-02 05:46:20.982 atags += ' ' * la
2025-07-02 05:46:20.988 btags += ' ' * lb
2025-07-02 05:46:20.995 else:
2025-07-02 05:46:21.003 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:21.011 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:21.017 else:
2025-07-02 05:46:21.022 # the synch pair is identical
2025-07-02 05:46:21.032 yield '  ' + aelt
2025-07-02 05:46:21.040
2025-07-02 05:46:21.047 # pump out diffs from after the synch point
2025-07-02 05:46:21.055 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:21.067
2025-07-02 05:46:21.075 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:21.082 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:21.088
2025-07-02 05:46:21.099 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:21.111 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:21.119 alo = 58, ahi = 1101
2025-07-02 05:46:21.128 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:21.134 blo = 58, bhi = 1101
2025-07-02 05:46:21.139
2025-07-02 05:46:21.144 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:21.149 g = []
2025-07-02 05:46:21.154 if alo < ahi:
2025-07-02 05:46:21.162 if blo < bhi:
2025-07-02 05:46:21.171 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:21.179 else:
2025-07-02 05:46:21.187 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:21.194 elif blo < bhi:
2025-07-02 05:46:21.201 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:21.207
2025-07-02 05:46:21.215 >       yield from g
2025-07-02 05:46:21.223
2025-07-02 05:46:21.231 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:21.243 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:21.252
2025-07-02 05:46:21.260 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:21.267 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:21.274 alo = 58, ahi = 1101
2025-07-02 05:46:21.285 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:21.297 blo = 58, bhi = 1101
2025-07-02 05:46:21.309
2025-07-02 05:46:21.320 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:21.334 r"""
2025-07-02 05:46:21.342 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:21.350 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:21.362 synch point, and intraline difference marking is done on the
2025-07-02 05:46:21.375 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:21.384
2025-07-02 05:46:21.392 Example:
2025-07-02 05:46:21.398
2025-07-02 05:46:21.405 >>> d = Differ()
2025-07-02 05:46:21.411 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:21.426 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:21.436 >>> print(''.join(results), end="")
2025-07-02 05:46:21.442 - abcDefghiJkl
2025-07-02 05:46:21.463 + abcdefGhijkl
2025-07-02 05:46:21.476 """
2025-07-02 05:46:21.482
2025-07-02 05:46:21.496 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:21.505 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:21.512 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:21.520 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:21.527 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:21.534
2025-07-02 05:46:21.539 # search for the pair that matches best without being identical
2025-07-02 05:46:21.546 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:21.551 # on junk -- unless we have to)
2025-07-02 05:46:21.559 for j in range(blo, bhi):
2025-07-02 05:46:21.569 bj = b[j]
2025-07-02 05:46:21.580 cruncher.set_seq2(bj)
2025-07-02 05:46:21.591 for i in range(alo, ahi):
2025-07-02 05:46:21.597 ai = a[i]
2025-07-02 05:46:21.604 if ai == bj:
2025-07-02 05:46:21.611 if eqi is None:
2025-07-02 05:46:21.617 eqi, eqj = i, j
2025-07-02 05:46:21.624 continue
2025-07-02 05:46:21.630 cruncher.set_seq1(ai)
2025-07-02 05:46:21.636 # computing similarity is expensive, so use the quick
2025-07-02 05:46:21.642 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:21.653 # compares by a factor of 3.
2025-07-02 05:46:21.669 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:21.677 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:21.685 # of the computation is cached by cruncher
2025-07-02 05:46:21.692 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:21.699 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:21.705 cruncher.ratio() > best_ratio:
2025-07-02 05:46:21.711 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:21.716 if best_ratio < cutoff:
2025-07-02 05:46:21.722 # no non-identical "pretty close" pair
2025-07-02 05:46:21.728 if eqi is None:
2025-07-02 05:46:21.734 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:21.743 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:21.751 return
2025-07-02 05:46:21.759 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:21.771 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:21.781 else:
2025-07-02 05:46:21.789 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:21.796 eqi = None
2025-07-02 05:46:21.802
2025-07-02 05:46:21.808 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:21.814 # identical
2025-07-02 05:46:21.824
2025-07-02 05:46:21.835 # pump out diffs from before the synch point
2025-07-02 05:46:21.842 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:21.853
2025-07-02 05:46:21.864 # do intraline marking on the synch pair
2025-07-02 05:46:21.873 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:21.881 if eqi is None:
2025-07-02 05:46:21.888 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:21.894 atags = btags = ""
2025-07-02 05:46:21.900 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:21.908 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:21.916 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:21.923 if tag == 'replace':
2025-07-02 05:46:21.932 atags += '^' * la
2025-07-02 05:46:21.944 btags += '^' * lb
2025-07-02 05:46:21.953 elif tag == 'delete':
2025-07-02 05:46:21.961 atags += '-' * la
2025-07-02 05:46:21.969 elif tag == 'insert':
2025-07-02 05:46:21.978 btags += '+' * lb
2025-07-02 05:46:21.984 elif tag == 'equal':
2025-07-02 05:46:21.998 atags += ' ' * la
2025-07-02 05:46:22.008 btags += ' ' * lb
2025-07-02 05:46:22.016 else:
2025-07-02 05:46:22.023 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:22.028 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:22.032 else:
2025-07-02 05:46:22.037 # the synch pair is identical
2025-07-02 05:46:22.043 yield '  ' + aelt
2025-07-02 05:46:22.048
2025-07-02 05:46:22.053 # pump out diffs from after the synch point
2025-07-02 05:46:22.058 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:22.062
2025-07-02 05:46:22.074 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:22.085 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:22.096
2025-07-02 05:46:22.109 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:22.118 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:22.125 alo = 59, ahi = 1101
2025-07-02 05:46:22.131 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:22.143 blo = 59, bhi = 1101
2025-07-02 05:46:22.152
2025-07-02 05:46:22.161 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:22.174 g = []
2025-07-02 05:46:22.185 if alo < ahi:
2025-07-02 05:46:22.195 if blo < bhi:
2025-07-02 05:46:22.208 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:22.220 else:
2025-07-02 05:46:22.233 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:22.246 elif blo < bhi:
2025-07-02 05:46:22.255 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:22.264
2025-07-02 05:46:22.270 >       yield from g
2025-07-02 05:46:22.277
2025-07-02 05:46:22.283 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:22.290 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:22.296
2025-07-02 05:46:22.302 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:22.311 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:22.318 alo = 59, ahi = 1101
2025-07-02 05:46:22.327 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:22.334 blo = 59, bhi = 1101
2025-07-02 05:46:22.340
2025-07-02 05:46:22.348 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:22.354 r"""
2025-07-02 05:46:22.360 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:22.367 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:22.380 synch point, and intraline difference marking is done on the
2025-07-02 05:46:22.391 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:22.404
2025-07-02 05:46:22.416 Example:
2025-07-02 05:46:22.429
2025-07-02 05:46:22.438 >>> d = Differ()
2025-07-02 05:46:22.446 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:22.453 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:22.459 >>> print(''.join(results), end="")
2025-07-02 05:46:22.465 - abcDefghiJkl
2025-07-02 05:46:22.479 + abcdefGhijkl
2025-07-02 05:46:22.500 """
2025-07-02 05:46:22.507
2025-07-02 05:46:22.512 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:22.518 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:22.523 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:22.532 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:22.543 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:22.550
2025-07-02 05:46:22.558 # search for the pair that matches best without being identical
2025-07-02 05:46:22.565 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:22.572 # on junk -- unless we have to)
2025-07-02 05:46:22.578 for j in range(blo, bhi):
2025-07-02 05:46:22.584 bj = b[j]
2025-07-02 05:46:22.590 cruncher.set_seq2(bj)
2025-07-02 05:46:22.597 for i in range(alo, ahi):
2025-07-02 05:46:22.605 ai = a[i]
2025-07-02 05:46:22.611 if ai == bj:
2025-07-02 05:46:22.618 if eqi is None:
2025-07-02 05:46:22.630 eqi, eqj = i, j
2025-07-02 05:46:22.638 continue
2025-07-02 05:46:22.646 cruncher.set_seq1(ai)
2025-07-02 05:46:22.659 # computing similarity is expensive, so use the quick
2025-07-02 05:46:22.666 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:22.671 # compares by a factor of 3.
2025-07-02 05:46:22.677 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:22.689 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:22.701 # of the computation is cached by cruncher
2025-07-02 05:46:22.715 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:22.725 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:22.737 cruncher.ratio() > best_ratio:
2025-07-02 05:46:22.747 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:22.755 if best_ratio < cutoff:
2025-07-02 05:46:22.764 # no non-identical "pretty close" pair
2025-07-02 05:46:22.775 if eqi is None:
2025-07-02 05:46:22.783 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:22.791 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:22.797 return
2025-07-02 05:46:22.804 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:22.811 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:22.817 else:
2025-07-02 05:46:22.824 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:22.830 eqi = None
2025-07-02 05:46:22.839
2025-07-02 05:46:22.851 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:22.863 # identical
2025-07-02 05:46:22.876
2025-07-02 05:46:22.887 # pump out diffs from before the synch point
2025-07-02 05:46:22.895 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:22.902
2025-07-02 05:46:22.911 # do intraline marking on the synch pair
2025-07-02 05:46:22.917 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:22.924 if eqi is None:
2025-07-02 05:46:22.930 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:22.937 atags = btags = ""
2025-07-02 05:46:22.943 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:22.955 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:22.966 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:22.977 if tag == 'replace':
2025-07-02 05:46:22.987 atags += '^' * la
2025-07-02 05:46:22.997 btags += '^' * lb
2025-07-02 05:46:23.004 elif tag == 'delete':
2025-07-02 05:46:23.013 atags += '-' * la
2025-07-02 05:46:23.021 elif tag == 'insert':
2025-07-02 05:46:23.029 btags += '+' * lb
2025-07-02 05:46:23.035 elif tag == 'equal':
2025-07-02 05:46:23.041 atags += ' ' * la
2025-07-02 05:46:23.046 btags += ' ' * lb
2025-07-02 05:46:23.052 else:
2025-07-02 05:46:23.058 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:23.064 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:23.070 else:
2025-07-02 05:46:23.081 # the synch pair is identical
2025-07-02 05:46:23.091 yield '  ' + aelt
2025-07-02 05:46:23.099
2025-07-02 05:46:23.107 # pump out diffs from after the synch point
2025-07-02 05:46:23.118 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:23.126
2025-07-02 05:46:23.133 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:23.140 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:23.146
2025-07-02 05:46:23.152 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:23.160 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:23.166 alo = 60, ahi = 1101
2025-07-02 05:46:23.174 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:23.180 blo = 60, bhi = 1101
2025-07-02 05:46:23.186
2025-07-02 05:46:23.192 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:23.198 g = []
2025-07-02 05:46:23.204 if alo < ahi:
2025-07-02 05:46:23.211 if blo < bhi:
2025-07-02 05:46:23.218 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:23.224 else:
2025-07-02 05:46:23.230 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:23.240 elif blo < bhi:
2025-07-02 05:46:23.251 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:23.261
2025-07-02 05:46:23.269 >       yield from g
2025-07-02 05:46:23.275
2025-07-02 05:46:23.282 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:23.291 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:23.301
2025-07-02 05:46:23.309 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:23.317 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:23.323 alo = 60, ahi = 1101
2025-07-02 05:46:23.330 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:23.336 blo = 60, bhi = 1101
2025-07-02 05:46:23.341
2025-07-02 05:46:23.348 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:23.354 r"""
2025-07-02 05:46:23.360 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:23.367 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:23.375 synch point, and intraline difference marking is done on the
2025-07-02 05:46:23.382 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:23.391
2025-07-02 05:46:23.404 Example:
2025-07-02 05:46:23.417
2025-07-02 05:46:23.428 >>> d = Differ()
2025-07-02 05:46:23.436 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:23.443 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:23.451 >>> print(''.join(results), end="")
2025-07-02 05:46:23.457 - abcDefghiJkl
2025-07-02 05:46:23.468 + abcdefGhijkl
2025-07-02 05:46:23.483 """
2025-07-02 05:46:23.490
2025-07-02 05:46:23.499 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:23.511 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:23.519 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:23.526 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:23.533 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:23.540
2025-07-02 05:46:23.547 # search for the pair that matches best without being identical
2025-07-02 05:46:23.557 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:23.570 # on junk -- unless we have to)
2025-07-02 05:46:23.578 for j in range(blo, bhi):
2025-07-02 05:46:23.590 bj = b[j]
2025-07-02 05:46:23.600 cruncher.set_seq2(bj)
2025-07-02 05:46:23.610 for i in range(alo, ahi):
2025-07-02 05:46:23.622 ai = a[i]
2025-07-02 05:46:23.632 if ai == bj:
2025-07-02 05:46:23.640 if eqi is None:
2025-07-02 05:46:23.648 eqi, eqj = i, j
2025-07-02 05:46:23.655 continue
2025-07-02 05:46:23.661 cruncher.set_seq1(ai)
2025-07-02 05:46:23.668 # computing similarity is expensive, so use the quick
2025-07-02 05:46:23.674 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:23.681 # compares by a factor of 3.
2025-07-02 05:46:23.688 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:23.694 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:23.701 # of the computation is cached by cruncher
2025-07-02 05:46:23.708 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:23.714 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:23.724 cruncher.ratio() > best_ratio:
2025-07-02 05:46:23.734 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:23.746 if best_ratio < cutoff:
2025-07-02 05:46:23.759 # no non-identical "pretty close" pair
2025-07-02 05:46:23.769 if eqi is None:
2025-07-02 05:46:23.779 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:23.787 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:23.795 return
2025-07-02 05:46:23.802 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:23.811 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:23.822 else:
2025-07-02 05:46:23.831 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:23.839 eqi = None
2025-07-02 05:46:23.846
2025-07-02 05:46:23.852 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:23.862 # identical
2025-07-02 05:46:23.876
2025-07-02 05:46:23.888 # pump out diffs from before the synch point
2025-07-02 05:46:23.897 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:23.911
2025-07-02 05:46:23.920 # do intraline marking on the synch pair
2025-07-02 05:46:23.929 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:23.942 if eqi is None:
2025-07-02 05:46:23.952 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:23.960 atags = btags = ""
2025-07-02 05:46:23.966 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:23.975 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:23.987 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:23.996 if tag == 'replace':
2025-07-02 05:46:24.004 atags += '^' * la
2025-07-02 05:46:24.011 btags += '^' * lb
2025-07-02 05:46:24.018 elif tag == 'delete':
2025-07-02 05:46:24.027 atags += '-' * la
2025-07-02 05:46:24.038 elif tag == 'insert':
2025-07-02 05:46:24.049 btags += '+' * lb
2025-07-02 05:46:24.057 elif tag == 'equal':
2025-07-02 05:46:24.063 atags += ' ' * la
2025-07-02 05:46:24.069 btags += ' ' * lb
2025-07-02 05:46:24.075 else:
2025-07-02 05:46:24.080 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:24.085 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:24.090 else:
2025-07-02 05:46:24.098 # the synch pair is identical
2025-07-02 05:46:24.106 yield '  ' + aelt
2025-07-02 05:46:24.114
2025-07-02 05:46:24.121 # pump out diffs from after the synch point
2025-07-02 05:46:24.135 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:24.148
2025-07-02 05:46:24.161 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:24.170 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:24.177
2025-07-02 05:46:24.182 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:24.187 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:24.192 alo = 61, ahi = 1101
2025-07-02 05:46:24.205 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:24.217 blo = 61, bhi = 1101
2025-07-02 05:46:24.231
2025-07-02 05:46:24.241 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:24.249 g = []
2025-07-02 05:46:24.256 if alo < ahi:
2025-07-02 05:46:24.262 if blo < bhi:
2025-07-02 05:46:24.272 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:24.281 else:
2025-07-02 05:46:24.289 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:24.295 elif blo < bhi:
2025-07-02 05:46:24.305 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:24.315
2025-07-02 05:46:24.324 >       yield from g
2025-07-02 05:46:24.331
2025-07-02 05:46:24.339 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:24.347 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:24.355
2025-07-02 05:46:24.363 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:24.372 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:24.379 alo = 61, ahi = 1101
2025-07-02 05:46:24.386 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:24.393 blo = 61, bhi = 1101
2025-07-02 05:46:24.400
2025-07-02 05:46:24.407 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:24.415 r"""
2025-07-02 05:46:24.422 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:24.429 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:24.436 synch point, and intraline difference marking is done on the
2025-07-02 05:46:24.444 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:24.450
2025-07-02 05:46:24.457 Example:
2025-07-02 05:46:24.463
2025-07-02 05:46:24.470 >>> d = Differ()
2025-07-02 05:46:24.477 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:24.484 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:24.493 >>> print(''.join(results), end="")
2025-07-02 05:46:24.503 - abcDefghiJkl
2025-07-02 05:46:24.529 + abcdefGhijkl
2025-07-02 05:46:24.550 """
2025-07-02 05:46:24.558
2025-07-02 05:46:24.565 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:24.571 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:24.577 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:24.583 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:24.590 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:24.600
2025-07-02 05:46:24.608 # search for the pair that matches best without being identical
2025-07-02 05:46:24.615 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:24.621 # on junk -- unless we have to)
2025-07-02 05:46:24.628 for j in range(blo, bhi):
2025-07-02 05:46:24.635 bj = b[j]
2025-07-02 05:46:24.646 cruncher.set_seq2(bj)
2025-07-02 05:46:24.658 for i in range(alo, ahi):
2025-07-02 05:46:24.670 ai = a[i]
2025-07-02 05:46:24.679 if ai == bj:
2025-07-02 05:46:24.688 if eqi is None:
2025-07-02 05:46:24.695 eqi, eqj = i, j
2025-07-02 05:46:24.702 continue
2025-07-02 05:46:24.709 cruncher.set_seq1(ai)
2025-07-02 05:46:24.724 # computing similarity is expensive, so use the quick
2025-07-02 05:46:24.733 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:24.745 # compares by a factor of 3.
2025-07-02 05:46:24.757 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:24.769 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:24.779 # of the computation is cached by cruncher
2025-07-02 05:46:24.789 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:24.797 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:24.803 cruncher.ratio() > best_ratio:
2025-07-02 05:46:24.809 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:24.816 if best_ratio < cutoff:
2025-07-02 05:46:24.822 # no non-identical "pretty close" pair
2025-07-02 05:46:24.828 if eqi is None:
2025-07-02 05:46:24.835 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:24.841 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:24.847 return
2025-07-02 05:46:24.853 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:24.862 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:24.874 else:
2025-07-02 05:46:24.885 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:24.896 eqi = None
2025-07-02 05:46:24.905
2025-07-02 05:46:24.915 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:24.924 # identical
2025-07-02 05:46:24.932
2025-07-02 05:46:24.945 # pump out diffs from before the synch point
2025-07-02 05:46:24.955 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:24.963
2025-07-02 05:46:24.970 # do intraline marking on the synch pair
2025-07-02 05:46:24.976 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:24.988 if eqi is None:
2025-07-02 05:46:24.997 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:25.005 atags = btags = ""
2025-07-02 05:46:25.011 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:25.017 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:25.021 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:25.027 if tag == 'replace':
2025-07-02 05:46:25.037 atags += '^' * la
2025-07-02 05:46:25.049 btags += '^' * lb
2025-07-02 05:46:25.061 elif tag == 'delete':
2025-07-02 05:46:25.072 atags += '-' * la
2025-07-02 05:46:25.080 elif tag == 'insert':
2025-07-02 05:46:25.087 btags += '+' * lb
2025-07-02 05:46:25.093 elif tag == 'equal':
2025-07-02 05:46:25.104 atags += ' ' * la
2025-07-02 05:46:25.113 btags += ' ' * lb
2025-07-02 05:46:25.123 else:
2025-07-02 05:46:25.131 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:25.139 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:25.148 else:
2025-07-02 05:46:25.160 # the synch pair is identical
2025-07-02 05:46:25.172 yield '  ' + aelt
2025-07-02 05:46:25.183
2025-07-02 05:46:25.193 # pump out diffs from after the synch point
2025-07-02 05:46:25.204 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:25.216
2025-07-02 05:46:25.224 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:25.231 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:25.237
2025-07-02 05:46:25.243 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:25.252 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:25.260 alo = 62, ahi = 1101
2025-07-02 05:46:25.272 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:25.281 blo = 62, bhi = 1101
2025-07-02 05:46:25.289
2025-07-02 05:46:25.295 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:25.302 g = []
2025-07-02 05:46:25.310 if alo < ahi:
2025-07-02 05:46:25.318 if blo < bhi:
2025-07-02 05:46:25.324 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:25.331 else:
2025-07-02 05:46:25.338 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:25.345 elif blo < bhi:
2025-07-02 05:46:25.351 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:25.357
2025-07-02 05:46:25.363 >       yield from g
2025-07-02 05:46:25.369
2025-07-02 05:46:25.376 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:25.382 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:25.388
2025-07-02 05:46:25.394 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:25.399 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:25.404 alo = 62, ahi = 1101
2025-07-02 05:46:25.410 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:25.418 blo = 62, bhi = 1101
2025-07-02 05:46:25.425
2025-07-02 05:46:25.432 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:25.440 r"""
2025-07-02 05:46:25.452 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:25.463 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:25.472 synch point, and intraline difference marking is done on the
2025-07-02 05:46:25.480 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:25.488
2025-07-02 05:46:25.495 Example:
2025-07-02 05:46:25.501
2025-07-02 05:46:25.508 >>> d = Differ()
2025-07-02 05:46:25.515 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:25.529 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:25.542 >>> print(''.join(results), end="")
2025-07-02 05:46:25.550 - abcDefghiJkl
2025-07-02 05:46:25.567 + abcdefGhijkl
2025-07-02 05:46:25.588 """
2025-07-02 05:46:25.597
2025-07-02 05:46:25.608 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:25.618 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:25.630 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:25.640 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:25.649 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:25.658
2025-07-02 05:46:25.670 # search for the pair that matches best without being identical
2025-07-02 05:46:25.679 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:25.687 # on junk -- unless we have to)
2025-07-02 05:46:25.695 for j in range(blo, bhi):
2025-07-02 05:46:25.706 bj = b[j]
2025-07-02 05:46:25.715 cruncher.set_seq2(bj)
2025-07-02 05:46:25.723 for i in range(alo, ahi):
2025-07-02 05:46:25.732 ai = a[i]
2025-07-02 05:46:25.742 if ai == bj:
2025-07-02 05:46:25.752 if eqi is None:
2025-07-02 05:46:25.759 eqi, eqj = i, j
2025-07-02 05:46:25.768 continue
2025-07-02 05:46:25.780 cruncher.set_seq1(ai)
2025-07-02 05:46:25.794 # computing similarity is expensive, so use the quick
2025-07-02 05:46:25.803 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:25.811 # compares by a factor of 3.
2025-07-02 05:46:25.819 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:25.827 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:25.835 # of the computation is cached by cruncher
2025-07-02 05:46:25.846 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:25.856 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:25.863 cruncher.ratio() > best_ratio:
2025-07-02 05:46:25.877 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:25.885 if best_ratio < cutoff:
2025-07-02 05:46:25.892 # no non-identical "pretty close" pair
2025-07-02 05:46:25.898 if eqi is None:
2025-07-02 05:46:25.908 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:25.916 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:25.921 return
2025-07-02 05:46:25.927 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:25.933 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:25.939 else:
2025-07-02 05:46:25.945 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:25.950 eqi = None
2025-07-02 05:46:25.960
2025-07-02 05:46:25.970 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:25.978 # identical
2025-07-02 05:46:25.984
2025-07-02 05:46:25.991 # pump out diffs from before the synch point
2025-07-02 05:46:25.998 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:26.011
2025-07-02 05:46:26.024 # do intraline marking on the synch pair
2025-07-02 05:46:26.038 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:26.047 if eqi is None:
2025-07-02 05:46:26.054 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:26.064 atags = btags = ""
2025-07-02 05:46:26.072 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:26.079 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:26.087 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:26.098 if tag == 'replace':
2025-07-02 05:46:26.109 atags += '^' * la
2025-07-02 05:46:26.115 btags += '^' * lb
2025-07-02 05:46:26.122 elif tag == 'delete':
2025-07-02 05:46:26.129 atags += '-' * la
2025-07-02 05:46:26.136 elif tag == 'insert':
2025-07-02 05:46:26.142 btags += '+' * lb
2025-07-02 05:46:26.149 elif tag == 'equal':
2025-07-02 05:46:26.155 atags += ' ' * la
2025-07-02 05:46:26.163 btags += ' ' * lb
2025-07-02 05:46:26.173 else:
2025-07-02 05:46:26.182 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:26.191 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:26.199 else:
2025-07-02 05:46:26.206 # the synch pair is identical
2025-07-02 05:46:26.215 yield '  ' + aelt
2025-07-02 05:46:26.222
2025-07-02 05:46:26.230 # pump out diffs from after the synch point
2025-07-02 05:46:26.237 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:26.243
2025-07-02 05:46:26.256 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:26.269 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:26.277
2025-07-02 05:46:26.292 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:26.305 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:26.316 alo = 63, ahi = 1101
2025-07-02 05:46:26.329 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:26.339 blo = 63, bhi = 1101
2025-07-02 05:46:26.347
2025-07-02 05:46:26.360 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:26.369 g = []
2025-07-02 05:46:26.381 if alo < ahi:
2025-07-02 05:46:26.391 if blo < bhi:
2025-07-02 05:46:26.404 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:26.415 else:
2025-07-02 05:46:26.424 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:26.431 elif blo < bhi:
2025-07-02 05:46:26.439 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:26.452
2025-07-02 05:46:26.461 >       yield from g
2025-07-02 05:46:26.469
2025-07-02 05:46:26.476 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:26.483 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:26.489
2025-07-02 05:46:26.500 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:26.510 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:26.517 alo = 63, ahi = 1101
2025-07-02 05:46:26.526 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:26.536 blo = 63, bhi = 1101
2025-07-02 05:46:26.543
2025-07-02 05:46:26.551 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:26.561 r"""
2025-07-02 05:46:26.568 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:26.574 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:26.581 synch point, and intraline difference marking is done on the
2025-07-02 05:46:26.593 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:26.604
2025-07-02 05:46:26.613 Example:
2025-07-02 05:46:26.620
2025-07-02 05:46:26.627 >>> d = Differ()
2025-07-02 05:46:26.634 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:26.639 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:26.644 >>> print(''.join(results), end="")
2025-07-02 05:46:26.650 - abcDefghiJkl
2025-07-02 05:46:26.669 + abcdefGhijkl
2025-07-02 05:46:26.692 """
2025-07-02 05:46:26.701
2025-07-02 05:46:26.709 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:26.716 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:26.722 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:26.733 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:26.742 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:26.749
2025-07-02 05:46:26.760 # search for the pair that matches best without being identical
2025-07-02 05:46:26.769 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:26.777 # on junk -- unless we have to)
2025-07-02 05:46:26.783 for j in range(blo, bhi):
2025-07-02 05:46:26.791 bj = b[j]
2025-07-02 05:46:26.797 cruncher.set_seq2(bj)
2025-07-02 05:46:26.807 for i in range(alo, ahi):
2025-07-02 05:46:26.814 ai = a[i]
2025-07-02 05:46:26.825 if ai == bj:
2025-07-02 05:46:26.835 if eqi is None:
2025-07-02 05:46:26.843 eqi, eqj = i, j
2025-07-02 05:46:26.850 continue
2025-07-02 05:46:26.856 cruncher.set_seq1(ai)
2025-07-02 05:46:26.861 # computing similarity is expensive, so use the quick
2025-07-02 05:46:26.868 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:26.874 # compares by a factor of 3.
2025-07-02 05:46:26.887 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:26.896 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:26.903 # of the computation is cached by cruncher
2025-07-02 05:46:26.910 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:26.919 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:26.925 cruncher.ratio() > best_ratio:
2025-07-02 05:46:26.931 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:26.938 if best_ratio < cutoff:
2025-07-02 05:46:26.943 # no non-identical "pretty close" pair
2025-07-02 05:46:26.950 if eqi is None:
2025-07-02 05:46:26.965 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:26.978 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:26.985 return
2025-07-02 05:46:26.993 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:27.000 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:27.013 else:
2025-07-02 05:46:27.024 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:27.035 eqi = None
2025-07-02 05:46:27.046
2025-07-02 05:46:27.055 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:27.064 # identical
2025-07-02 05:46:27.071
2025-07-02 05:46:27.078 # pump out diffs from before the synch point
2025-07-02 05:46:27.089 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:27.097
2025-07-02 05:46:27.104 # do intraline marking on the synch pair
2025-07-02 05:46:27.110 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:27.121 if eqi is None:
2025-07-02 05:46:27.131 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:27.137 atags = btags = ""
2025-07-02 05:46:27.143 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:27.150 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:27.157 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:27.163 if tag == 'replace':
2025-07-02 05:46:27.169 atags += '^' * la
2025-07-02 05:46:27.175 btags += '^' * lb
2025-07-02 05:46:27.182 elif tag == 'delete':
2025-07-02 05:46:27.191 atags += '-' * la
2025-07-02 05:46:27.203 elif tag == 'insert':
2025-07-02 05:46:27.214 btags += '+' * lb
2025-07-02 05:46:27.227 elif tag == 'equal':
2025-07-02 05:46:27.236 atags += ' ' * la
2025-07-02 05:46:27.245 btags += ' ' * lb
2025-07-02 05:46:27.252 else:
2025-07-02 05:46:27.257 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:27.263 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:27.269 else:
2025-07-02 05:46:27.277 # the synch pair is identical
2025-07-02 05:46:27.284 yield '  ' + aelt
2025-07-02 05:46:27.291
2025-07-02 05:46:27.299 # pump out diffs from after the synch point
2025-07-02 05:46:27.311 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:27.321
2025-07-02 05:46:27.328 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:27.335 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:27.347
2025-07-02 05:46:27.359 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:27.371 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:27.381 alo = 64, ahi = 1101
2025-07-02 05:46:27.390 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:27.396 blo = 64, bhi = 1101
2025-07-02 05:46:27.405
2025-07-02 05:46:27.418 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:27.429 g = []
2025-07-02 05:46:27.437 if alo < ahi:
2025-07-02 05:46:27.445 if blo < bhi:
2025-07-02 05:46:27.451 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:27.457 else:
2025-07-02 05:46:27.462 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:27.466 elif blo < bhi:
2025-07-02 05:46:27.472 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:27.479
2025-07-02 05:46:27.487 >       yield from g
2025-07-02 05:46:27.494
2025-07-02 05:46:27.506 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:27.514 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:27.524
2025-07-02 05:46:27.531 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:27.539 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:27.547 alo = 64, ahi = 1101
2025-07-02 05:46:27.556 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:27.563 blo = 64, bhi = 1101
2025-07-02 05:46:27.571
2025-07-02 05:46:27.578 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:27.584 r"""
2025-07-02 05:46:27.591 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:27.598 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:27.609 synch point, and intraline difference marking is done on the
2025-07-02 05:46:27.619 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:27.628
2025-07-02 05:46:27.635 Example:
2025-07-02 05:46:27.641
2025-07-02 05:46:27.646 >>> d = Differ()
2025-07-02 05:46:27.651 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:27.655 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:27.660 >>> print(''.join(results), end="")
2025-07-02 05:46:27.665 - abcDefghiJkl
2025-07-02 05:46:27.673 + abcdefGhijkl
2025-07-02 05:46:27.683 """
2025-07-02 05:46:27.688
2025-07-02 05:46:27.692 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:27.697 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:27.701 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:27.706 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:27.710 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:27.715
2025-07-02 05:46:27.719 # search for the pair that matches best without being identical
2025-07-02 05:46:27.724 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:27.728 # on junk -- unless we have to)
2025-07-02 05:46:27.733 for j in range(blo, bhi):
2025-07-02 05:46:27.737 bj = b[j]
2025-07-02 05:46:27.742 cruncher.set_seq2(bj)
2025-07-02 05:46:27.746 for i in range(alo, ahi):
2025-07-02 05:46:27.751 ai = a[i]
2025-07-02 05:46:27.755 if ai == bj:
2025-07-02 05:46:27.760 if eqi is None:
2025-07-02 05:46:27.764 eqi, eqj = i, j
2025-07-02 05:46:27.768 continue
2025-07-02 05:46:27.773 cruncher.set_seq1(ai)
2025-07-02 05:46:27.777 # computing similarity is expensive, so use the quick
2025-07-02 05:46:27.782 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:27.788 # compares by a factor of 3.
2025-07-02 05:46:27.793 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:27.798 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:27.802 # of the computation is cached by cruncher
2025-07-02 05:46:27.807 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:27.816 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:27.827 cruncher.ratio() > best_ratio:
2025-07-02 05:46:27.836 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:27.843 if best_ratio < cutoff:
2025-07-02 05:46:27.851 # no non-identical "pretty close" pair
2025-07-02 05:46:27.863 if eqi is None:
2025-07-02 05:46:27.875 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:27.887 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:27.896 return
2025-07-02 05:46:27.904 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:27.911 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:27.917 else:
2025-07-02 05:46:27.924 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:27.930 eqi = None
2025-07-02 05:46:27.940
2025-07-02 05:46:27.952 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:27.963 # identical
2025-07-02 05:46:27.975
2025-07-02 05:46:27.984 # pump out diffs from before the synch point
2025-07-02 05:46:27.991 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:27.997
2025-07-02 05:46:28.007 # do intraline marking on the synch pair
2025-07-02 05:46:28.022 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:28.033 if eqi is None:
2025-07-02 05:46:28.046 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:28.057 atags = btags = ""
2025-07-02 05:46:28.068 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:28.080 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:28.089 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:28.096 if tag == 'replace':
2025-07-02 05:46:28.102 atags += '^' * la
2025-07-02 05:46:28.107 btags += '^' * lb
2025-07-02 05:46:28.112 elif tag == 'delete':
2025-07-02 05:46:28.117 atags += '-' * la
2025-07-02 05:46:28.124 elif tag == 'insert':
2025-07-02 05:46:28.130 btags += '+' * lb
2025-07-02 05:46:28.136 elif tag == 'equal':
2025-07-02 05:46:28.142 atags += ' ' * la
2025-07-02 05:46:28.148 btags += ' ' * lb
2025-07-02 05:46:28.155 else:
2025-07-02 05:46:28.164 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:28.175 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:28.184 else:
2025-07-02 05:46:28.192 # the synch pair is identical
2025-07-02 05:46:28.199 yield '  ' + aelt
2025-07-02 05:46:28.208
2025-07-02 05:46:28.216 # pump out diffs from after the synch point
2025-07-02 05:46:28.225 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:28.232
2025-07-02 05:46:28.239 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:28.248 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:28.255
2025-07-02 05:46:28.263 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:28.272 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:28.279 alo = 65, ahi = 1101
2025-07-02 05:46:28.289 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:28.300 blo = 65, bhi = 1101
2025-07-02 05:46:28.311
2025-07-02 05:46:28.319 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:28.334 g = []
2025-07-02 05:46:28.344 if alo < ahi:
2025-07-02 05:46:28.352 if blo < bhi:
2025-07-02 05:46:28.358 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:28.364 else:
2025-07-02 05:46:28.371 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:28.379 elif blo < bhi:
2025-07-02 05:46:28.387 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:28.396
2025-07-02 05:46:28.408 >       yield from g
2025-07-02 05:46:28.421
2025-07-02 05:46:28.429 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:28.435 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:28.447
2025-07-02 05:46:28.456 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:28.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:28.476 alo = 65, ahi = 1101
2025-07-02 05:46:28.484 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:28.491 blo = 65, bhi = 1101
2025-07-02 05:46:28.498
2025-07-02 05:46:28.509 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:28.521 r"""
2025-07-02 05:46:28.532 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:28.543 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:28.551 synch point, and intraline difference marking is done on the
2025-07-02 05:46:28.557 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:28.562
2025-07-02 05:46:28.570 Example:
2025-07-02 05:46:28.577
2025-07-02 05:46:28.584 >>> d = Differ()
2025-07-02 05:46:28.592 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:28.600 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:28.607 >>> print(''.join(results), end="")
2025-07-02 05:46:28.614 - abcDefghiJkl
2025-07-02 05:46:28.634 + abcdefGhijkl
2025-07-02 05:46:28.659 """
2025-07-02 05:46:28.670
2025-07-02 05:46:28.679 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:28.686 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:28.695 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:28.703 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:28.711 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:28.718
2025-07-02 05:46:28.726 # search for the pair that matches best without being identical
2025-07-02 05:46:28.735 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:28.748 # on junk -- unless we have to)
2025-07-02 05:46:28.757 for j in range(blo, bhi):
2025-07-02 05:46:28.765 bj = b[j]
2025-07-02 05:46:28.776 cruncher.set_seq2(bj)
2025-07-02 05:46:28.786 for i in range(alo, ahi):
2025-07-02 05:46:28.797 ai = a[i]
2025-07-02 05:46:28.810 if ai == bj:
2025-07-02 05:46:28.818 if eqi is None:
2025-07-02 05:46:28.827 eqi, eqj = i, j
2025-07-02 05:46:28.839 continue
2025-07-02 05:46:28.848 cruncher.set_seq1(ai)
2025-07-02 05:46:28.855 # computing similarity is expensive, so use the quick
2025-07-02 05:46:28.863 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:28.870 # compares by a factor of 3.
2025-07-02 05:46:28.877 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:28.884 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:28.890 # of the computation is cached by cruncher
2025-07-02 05:46:28.897 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:28.903 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:28.911 cruncher.ratio() > best_ratio:
2025-07-02 05:46:28.924 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:28.933 if best_ratio < cutoff:
2025-07-02 05:46:28.941 # no non-identical "pretty close" pair
2025-07-02 05:46:28.947 if eqi is None:
2025-07-02 05:46:28.953 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:28.958 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:28.964 return
2025-07-02 05:46:28.972 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:28.984 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:28.993 else:
2025-07-02 05:46:29.003 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:29.012 eqi = None
2025-07-02 05:46:29.019
2025-07-02 05:46:29.027 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:29.036 # identical
2025-07-02 05:46:29.046
2025-07-02 05:46:29.056 # pump out diffs from before the synch point
2025-07-02 05:46:29.064 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:29.070
2025-07-02 05:46:29.082 # do intraline marking on the synch pair
2025-07-02 05:46:29.091 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:29.099 if eqi is None:
2025-07-02 05:46:29.107 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:29.113 atags = btags = ""
2025-07-02 05:46:29.119 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:29.126 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:29.132 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:29.139 if tag == 'replace':
2025-07-02 05:46:29.149 atags += '^' * la
2025-07-02 05:46:29.158 btags += '^' * lb
2025-07-02 05:46:29.167 elif tag == 'delete':
2025-07-02 05:46:29.176 atags += '-' * la
2025-07-02 05:46:29.185 elif tag == 'insert':
2025-07-02 05:46:29.191 btags += '+' * lb
2025-07-02 05:46:29.198 elif tag == 'equal':
2025-07-02 05:46:29.202 atags += ' ' * la
2025-07-02 05:46:29.207 btags += ' ' * lb
2025-07-02 05:46:29.211 else:
2025-07-02 05:46:29.216 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:29.220 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:29.224 else:
2025-07-02 05:46:29.229 # the synch pair is identical
2025-07-02 05:46:29.233 yield '  ' + aelt
2025-07-02 05:46:29.238
2025-07-02 05:46:29.242 # pump out diffs from after the synch point
2025-07-02 05:46:29.246 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:29.251
2025-07-02 05:46:29.255 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:29.260 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:29.264
2025-07-02 05:46:29.269 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:29.274 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:29.278 alo = 66, ahi = 1101
2025-07-02 05:46:29.283 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:29.288 blo = 66, bhi = 1101
2025-07-02 05:46:29.292
2025-07-02 05:46:29.298 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:29.309 g = []
2025-07-02 05:46:29.319 if alo < ahi:
2025-07-02 05:46:29.327 if blo < bhi:
2025-07-02 05:46:29.334 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:29.340 else:
2025-07-02 05:46:29.346 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:29.352 elif blo < bhi:
2025-07-02 05:46:29.358 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:29.369
2025-07-02 05:46:29.380 >       yield from g
2025-07-02 05:46:29.388
2025-07-02 05:46:29.393 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:29.400 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:29.405
2025-07-02 05:46:29.410 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:29.418 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:29.424 alo = 66, ahi = 1101
2025-07-02 05:46:29.433 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:29.439 blo = 66, bhi = 1101
2025-07-02 05:46:29.444
2025-07-02 05:46:29.451 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:29.464 r"""
2025-07-02 05:46:29.475 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:29.484 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:29.499 synch point, and intraline difference marking is done on the
2025-07-02 05:46:29.509 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:29.518
2025-07-02 05:46:29.530 Example:
2025-07-02 05:46:29.542
2025-07-02 05:46:29.551 >>> d = Differ()
2025-07-02 05:46:29.559 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:29.572 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:29.586 >>> print(''.join(results), end="")
2025-07-02 05:46:29.597 - abcDefghiJkl
2025-07-02 05:46:29.615 + abcdefGhijkl
2025-07-02 05:46:29.627 """
2025-07-02 05:46:29.633
2025-07-02 05:46:29.647 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:29.657 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:29.666 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:29.673 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:29.680 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:29.687
2025-07-02 05:46:29.696 # search for the pair that matches best without being identical
2025-07-02 05:46:29.704 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:29.711 # on junk -- unless we have to)
2025-07-02 05:46:29.719 for j in range(blo, bhi):
2025-07-02 05:46:29.731 bj = b[j]
2025-07-02 05:46:29.740 cruncher.set_seq2(bj)
2025-07-02 05:46:29.747 for i in range(alo, ahi):
2025-07-02 05:46:29.754 ai = a[i]
2025-07-02 05:46:29.768 if ai == bj:
2025-07-02 05:46:29.778 if eqi is None:
2025-07-02 05:46:29.786 eqi, eqj = i, j
2025-07-02 05:46:29.793 continue
2025-07-02 05:46:29.805 cruncher.set_seq1(ai)
2025-07-02 05:46:29.814 # computing similarity is expensive, so use the quick
2025-07-02 05:46:29.821 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:29.828 # compares by a factor of 3.
2025-07-02 05:46:29.840 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:29.854 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:29.866 # of the computation is cached by cruncher
2025-07-02 05:46:29.878 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:29.887 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:29.895 cruncher.ratio() > best_ratio:
2025-07-02 05:46:29.902 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:29.913 if best_ratio < cutoff:
2025-07-02 05:46:29.924 # no non-identical "pretty close" pair
2025-07-02 05:46:29.933 if eqi is None:
2025-07-02 05:46:29.941 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:29.947 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:29.954 return
2025-07-02 05:46:29.961 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:29.974 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:29.985 else:
2025-07-02 05:46:29.994 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:30.003 eqi = None
2025-07-02 05:46:30.009
2025-07-02 05:46:30.016 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:30.021 # identical
2025-07-02 05:46:30.027
2025-07-02 05:46:30.037 # pump out diffs from before the synch point
2025-07-02 05:46:30.043 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:30.056
2025-07-02 05:46:30.068 # do intraline marking on the synch pair
2025-07-02 05:46:30.078 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:30.089 if eqi is None:
2025-07-02 05:46:30.099 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:30.107 atags = btags = ""
2025-07-02 05:46:30.115 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:30.124 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:30.136 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:30.145 if tag == 'replace':
2025-07-02 05:46:30.154 atags += '^' * la
2025-07-02 05:46:30.168 btags += '^' * lb
2025-07-02 05:46:30.176 elif tag == 'delete':
2025-07-02 05:46:30.183 atags += '-' * la
2025-07-02 05:46:30.193 elif tag == 'insert':
2025-07-02 05:46:30.206 btags += '+' * lb
2025-07-02 05:46:30.215 elif tag == 'equal':
2025-07-02 05:46:30.223 atags += ' ' * la
2025-07-02 05:46:30.230 btags += ' ' * lb
2025-07-02 05:46:30.237 else:
2025-07-02 05:46:30.245 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:30.252 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:30.258 else:
2025-07-02 05:46:30.263 # the synch pair is identical
2025-07-02 05:46:30.271 yield '  ' + aelt
2025-07-02 05:46:30.278
2025-07-02 05:46:30.285 # pump out diffs from after the synch point
2025-07-02 05:46:30.293 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:30.303
2025-07-02 05:46:30.312 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:30.319 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:30.326
2025-07-02 05:46:30.332 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:30.337 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:30.342 alo = 67, ahi = 1101
2025-07-02 05:46:30.347 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:30.351 blo = 67, bhi = 1101
2025-07-02 05:46:30.356
2025-07-02 05:46:30.360 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:30.365 g = []
2025-07-02 05:46:30.369 if alo < ahi:
2025-07-02 05:46:30.374 if blo < bhi:
2025-07-02 05:46:30.378 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:30.383 else:
2025-07-02 05:46:30.387 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:30.392 elif blo < bhi:
2025-07-02 05:46:30.396 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:30.401
2025-07-02 05:46:30.405 >       yield from g
2025-07-02 05:46:30.410
2025-07-02 05:46:30.414 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:30.419 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:30.424
2025-07-02 05:46:30.428 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:30.433 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:30.438 alo = 67, ahi = 1101
2025-07-02 05:46:30.444 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:30.450 blo = 67, bhi = 1101
2025-07-02 05:46:30.455
2025-07-02 05:46:30.461 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:30.467 r"""
2025-07-02 05:46:30.473 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:30.479 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:30.484 synch point, and intraline difference marking is done on the
2025-07-02 05:46:30.489 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:30.494
2025-07-02 05:46:30.499 Example:
2025-07-02 05:46:30.508
2025-07-02 05:46:30.520 >>> d = Differ()
2025-07-02 05:46:30.530 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:30.538 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:30.543 >>> print(''.join(results), end="")
2025-07-02 05:46:30.549 - abcDefghiJkl
2025-07-02 05:46:30.559 + abcdefGhijkl
2025-07-02 05:46:30.570 """
2025-07-02 05:46:30.581
2025-07-02 05:46:30.590 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:30.600 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:30.607 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:30.615 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:30.622 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:30.632
2025-07-02 05:46:30.642 # search for the pair that matches best without being identical
2025-07-02 05:46:30.651 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:30.662 # on junk -- unless we have to)
2025-07-02 05:46:30.675 for j in range(blo, bhi):
2025-07-02 05:46:30.685 bj = b[j]
2025-07-02 05:46:30.692 cruncher.set_seq2(bj)
2025-07-02 05:46:30.699 for i in range(alo, ahi):
2025-07-02 05:46:30.707 ai = a[i]
2025-07-02 05:46:30.718 if ai == bj:
2025-07-02 05:46:30.727 if eqi is None:
2025-07-02 05:46:30.738 eqi, eqj = i, j
2025-07-02 05:46:30.752 continue
2025-07-02 05:46:30.762 cruncher.set_seq1(ai)
2025-07-02 05:46:30.776 # computing similarity is expensive, so use the quick
2025-07-02 05:46:30.789 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:30.800 # compares by a factor of 3.
2025-07-02 05:46:30.812 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:30.822 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:30.833 # of the computation is cached by cruncher
2025-07-02 05:46:30.839 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:30.847 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:30.855 cruncher.ratio() > best_ratio:
2025-07-02 05:46:30.863 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:30.870 if best_ratio < cutoff:
2025-07-02 05:46:30.877 # no non-identical "pretty close" pair
2025-07-02 05:46:30.883 if eqi is None:
2025-07-02 05:46:30.889 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:30.894 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:30.900 return
2025-07-02 05:46:30.905 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:30.911 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:30.917 else:
2025-07-02 05:46:30.923 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:30.929 eqi = None
2025-07-02 05:46:30.940
2025-07-02 05:46:30.949 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:30.957 # identical
2025-07-02 05:46:30.963
2025-07-02 05:46:30.973 # pump out diffs from before the synch point
2025-07-02 05:46:30.984 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:30.995
2025-07-02 05:46:31.002 # do intraline marking on the synch pair
2025-07-02 05:46:31.009 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:31.015 if eqi is None:
2025-07-02 05:46:31.024 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:31.031 atags = btags = ""
2025-07-02 05:46:31.037 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:31.046 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:31.060 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:31.072 if tag == 'replace':
2025-07-02 05:46:31.081 atags += '^' * la
2025-07-02 05:46:31.091 btags += '^' * lb
2025-07-02 05:46:31.101 elif tag == 'delete':
2025-07-02 05:46:31.109 atags += '-' * la
2025-07-02 05:46:31.120 elif tag == 'insert':
2025-07-02 05:46:31.131 btags += '+' * lb
2025-07-02 05:46:31.139 elif tag == 'equal':
2025-07-02 05:46:31.146 atags += ' ' * la
2025-07-02 05:46:31.158 btags += ' ' * lb
2025-07-02 05:46:31.167 else:
2025-07-02 05:46:31.175 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:31.182 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:31.194 else:
2025-07-02 05:46:31.205 # the synch pair is identical
2025-07-02 05:46:31.213 yield '  ' + aelt
2025-07-02 05:46:31.221
2025-07-02 05:46:31.229 # pump out diffs from after the synch point
2025-07-02 05:46:31.242 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:31.253
2025-07-02 05:46:31.264 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:31.273 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:31.280
2025-07-02 05:46:31.288 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:31.295 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:31.301 alo = 70, ahi = 1101
2025-07-02 05:46:31.308 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:31.314 blo = 70, bhi = 1101
2025-07-02 05:46:31.320
2025-07-02 05:46:31.327 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:31.333 g = []
2025-07-02 05:46:31.339 if alo < ahi:
2025-07-02 05:46:31.345 if blo < bhi:
2025-07-02 05:46:31.352 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:31.358 else:
2025-07-02 05:46:31.365 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:31.371 elif blo < bhi:
2025-07-02 05:46:31.380 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:31.390
2025-07-02 05:46:31.399 >       yield from g
2025-07-02 05:46:31.406
2025-07-02 05:46:31.415 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:31.424 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:31.437
2025-07-02 05:46:31.447 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:31.460 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:31.470 alo = 70, ahi = 1101
2025-07-02 05:46:31.477 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:31.483 blo = 70, bhi = 1101
2025-07-02 05:46:31.489
2025-07-02 05:46:31.501 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:31.507 r"""
2025-07-02 05:46:31.514 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:31.523 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:31.533 synch point, and intraline difference marking is done on the
2025-07-02 05:46:31.545 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:31.556
2025-07-02 05:46:31.563 Example:
2025-07-02 05:46:31.570
2025-07-02 05:46:31.576 >>> d = Differ()
2025-07-02 05:46:31.582 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:31.591 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:31.603 >>> print(''.join(results), end="")
2025-07-02 05:46:31.611 - abcDefghiJkl
2025-07-02 05:46:31.625 + abcdefGhijkl
2025-07-02 05:46:31.640 """
2025-07-02 05:46:31.652
2025-07-02 05:46:31.663 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:31.673 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:31.686 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:31.699 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:31.711 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:31.720
2025-07-02 05:46:31.728 # search for the pair that matches best without being identical
2025-07-02 05:46:31.735 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:31.742 # on junk -- unless we have to)
2025-07-02 05:46:31.749 for j in range(blo, bhi):
2025-07-02 05:46:31.755 bj = b[j]
2025-07-02 05:46:31.762 cruncher.set_seq2(bj)
2025-07-02 05:46:31.773 for i in range(alo, ahi):
2025-07-02 05:46:31.782 ai = a[i]
2025-07-02 05:46:31.792 if ai == bj:
2025-07-02 05:46:31.802 if eqi is None:
2025-07-02 05:46:31.811 eqi, eqj = i, j
2025-07-02 05:46:31.819 continue
2025-07-02 05:46:31.827 cruncher.set_seq1(ai)
2025-07-02 05:46:31.834 # computing similarity is expensive, so use the quick
2025-07-02 05:46:31.841 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:31.848 # compares by a factor of 3.
2025-07-02 05:46:31.855 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:31.867 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:31.876 # of the computation is cached by cruncher
2025-07-02 05:46:31.884 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:31.893 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:31.900 cruncher.ratio() > best_ratio:
2025-07-02 05:46:31.908 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:31.915 if best_ratio < cutoff:
2025-07-02 05:46:31.923 # no non-identical "pretty close" pair
2025-07-02 05:46:31.931 if eqi is None:
2025-07-02 05:46:31.943 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:31.952 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:31.960 return
2025-07-02 05:46:31.970 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:31.982 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:31.994 else:
2025-07-02 05:46:32.003 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:32.011 eqi = None
2025-07-02 05:46:32.018
2025-07-02 05:46:32.026 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:32.038 # identical
2025-07-02 05:46:32.048
2025-07-02 05:46:32.055 # pump out diffs from before the synch point
2025-07-02 05:46:32.061 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:32.067
2025-07-02 05:46:32.075 # do intraline marking on the synch pair
2025-07-02 05:46:32.081 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:32.088 if eqi is None:
2025-07-02 05:46:32.095 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:32.104 atags = btags = ""
2025-07-02 05:46:32.114 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:32.121 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:32.128 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:32.136 if tag == 'replace':
2025-07-02 05:46:32.143 atags += '^' * la
2025-07-02 05:46:32.150 btags += '^' * lb
2025-07-02 05:46:32.159 elif tag == 'delete':
2025-07-02 05:46:32.169 atags += '-' * la
2025-07-02 05:46:32.177 elif tag == 'insert':
2025-07-02 05:46:32.183 btags += '+' * lb
2025-07-02 05:46:32.189 elif tag == 'equal':
2025-07-02 05:46:32.194 atags += ' ' * la
2025-07-02 05:46:32.199 btags += ' ' * lb
2025-07-02 05:46:32.204 else:
2025-07-02 05:46:32.209 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:32.214 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:32.219 else:
2025-07-02 05:46:32.226 # the synch pair is identical
2025-07-02 05:46:32.232 yield '  ' + aelt
2025-07-02 05:46:32.238
2025-07-02 05:46:32.245 # pump out diffs from after the synch point
2025-07-02 05:46:32.252 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:32.257
2025-07-02 05:46:32.269 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:32.278 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:32.286
2025-07-02 05:46:32.293 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:32.306 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:32.313 alo = 71, ahi = 1101
2025-07-02 05:46:32.323 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:32.331 blo = 71, bhi = 1101
2025-07-02 05:46:32.339
2025-07-02 05:46:32.347 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:32.355 g = []
2025-07-02 05:46:32.363 if alo < ahi:
2025-07-02 05:46:32.374 if blo < bhi:
2025-07-02 05:46:32.383 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:32.390 else:
2025-07-02 05:46:32.398 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:32.406 elif blo < bhi:
2025-07-02 05:46:32.412 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:32.418
2025-07-02 05:46:32.426 >       yield from g
2025-07-02 05:46:32.433
2025-07-02 05:46:32.441 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:32.449 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:32.457
2025-07-02 05:46:32.465 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:32.476 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:32.487 alo = 71, ahi = 1101
2025-07-02 05:46:32.497 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:32.504 blo = 71, bhi = 1101
2025-07-02 05:46:32.510
2025-07-02 05:46:32.516 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:32.522 r"""
2025-07-02 05:46:32.530 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:32.538 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:32.545 synch point, and intraline difference marking is done on the
2025-07-02 05:46:32.551 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:32.556
2025-07-02 05:46:32.562 Example:
2025-07-02 05:46:32.569
2025-07-02 05:46:32.576 >>> d = Differ()
2025-07-02 05:46:32.585 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:32.592 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:32.600 >>> print(''.join(results), end="")
2025-07-02 05:46:32.607 - abcDefghiJkl
2025-07-02 05:46:32.622 + abcdefGhijkl
2025-07-02 05:46:32.642 """
2025-07-02 05:46:32.653
2025-07-02 05:46:32.660 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:32.667 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:32.673 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:32.679 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:32.692 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:32.702
2025-07-02 05:46:32.712 # search for the pair that matches best without being identical
2025-07-02 05:46:32.720 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:32.727 # on junk -- unless we have to)
2025-07-02 05:46:32.732 for j in range(blo, bhi):
2025-07-02 05:46:32.736 bj = b[j]
2025-07-02 05:46:32.741 cruncher.set_seq2(bj)
2025-07-02 05:46:32.746 for i in range(alo, ahi):
2025-07-02 05:46:32.750 ai = a[i]
2025-07-02 05:46:32.755 if ai == bj:
2025-07-02 05:46:32.760 if eqi is None:
2025-07-02 05:46:32.764 eqi, eqj = i, j
2025-07-02 05:46:32.769 continue
2025-07-02 05:46:32.773 cruncher.set_seq1(ai)
2025-07-02 05:46:32.779 # computing similarity is expensive, so use the quick
2025-07-02 05:46:32.786 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:32.790 # compares by a factor of 3.
2025-07-02 05:46:32.795 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:32.799 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:32.804 # of the computation is cached by cruncher
2025-07-02 05:46:32.809 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:32.814 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:32.821 cruncher.ratio() > best_ratio:
2025-07-02 05:46:32.828 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:32.834 if best_ratio < cutoff:
2025-07-02 05:46:32.844 # no non-identical "pretty close" pair
2025-07-02 05:46:32.852 if eqi is None:
2025-07-02 05:46:32.859 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:32.871 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:32.882 return
2025-07-02 05:46:32.893 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:32.902 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:32.909 else:
2025-07-02 05:46:32.916 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:32.922 eqi = None
2025-07-02 05:46:32.927
2025-07-02 05:46:32.934 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:32.943 # identical
2025-07-02 05:46:32.955
2025-07-02 05:46:32.963 # pump out diffs from before the synch point
2025-07-02 05:46:32.971 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:32.976
2025-07-02 05:46:32.981 # do intraline marking on the synch pair
2025-07-02 05:46:32.987 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:32.992 if eqi is None:
2025-07-02 05:46:33.000 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:33.007 atags = btags = ""
2025-07-02 05:46:33.015 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:33.024 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:33.036 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:33.045 if tag == 'replace':
2025-07-02 05:46:33.052 atags += '^' * la
2025-07-02 05:46:33.059 btags += '^' * lb
2025-07-02 05:46:33.066 elif tag == 'delete':
2025-07-02 05:46:33.076 atags += '-' * la
2025-07-02 05:46:33.086 elif tag == 'insert':
2025-07-02 05:46:33.097 btags += '+' * lb
2025-07-02 05:46:33.109 elif tag == 'equal':
2025-07-02 05:46:33.120 atags += ' ' * la
2025-07-02 05:46:33.130 btags += ' ' * lb
2025-07-02 05:46:33.141 else:
2025-07-02 05:46:33.151 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:33.161 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:33.168 else:
2025-07-02 05:46:33.176 # the synch pair is identical
2025-07-02 05:46:33.183 yield '  ' + aelt
2025-07-02 05:46:33.189
2025-07-02 05:46:33.200 # pump out diffs from after the synch point
2025-07-02 05:46:33.209 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:33.216
2025-07-02 05:46:33.223 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:33.231 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:33.240
2025-07-02 05:46:33.248 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:33.256 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:33.262 alo = 72, ahi = 1101
2025-07-02 05:46:33.269 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:33.275 blo = 72, bhi = 1101
2025-07-02 05:46:33.280
2025-07-02 05:46:33.286 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:33.293 g = []
2025-07-02 05:46:33.298 if alo < ahi:
2025-07-02 05:46:33.304 if blo < bhi:
2025-07-02 05:46:33.310 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:33.315 else:
2025-07-02 05:46:33.322 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:33.328 elif blo < bhi:
2025-07-02 05:46:33.336 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:33.343
2025-07-02 05:46:33.351 >       yield from g
2025-07-02 05:46:33.359
2025-07-02 05:46:33.372 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:33.381 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:33.388
2025-07-02 05:46:33.394 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:33.401 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:33.406 alo = 72, ahi = 1101
2025-07-02 05:46:33.413 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:33.419 blo = 72, bhi = 1101
2025-07-02 05:46:33.424
2025-07-02 05:46:33.432 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:33.438 r"""
2025-07-02 05:46:33.444 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:33.449 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:33.456 synch point, and intraline difference marking is done on the
2025-07-02 05:46:33.462 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:33.468
2025-07-02 05:46:33.474 Example:
2025-07-02 05:46:33.483
2025-07-02 05:46:33.490 >>> d = Differ()
2025-07-02 05:46:33.498 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:33.505 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:33.513 >>> print(''.join(results), end="")
2025-07-02 05:46:33.518 - abcDefghiJkl
2025-07-02 05:46:33.529 + abcdefGhijkl
2025-07-02 05:46:33.542 """
2025-07-02 05:46:33.549
2025-07-02 05:46:33.563 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:33.571 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:33.581 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:33.590 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:33.598 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:33.610
2025-07-02 05:46:33.623 # search for the pair that matches best without being identical
2025-07-02 05:46:33.636 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:33.649 # on junk -- unless we have to)
2025-07-02 05:46:33.660 for j in range(blo, bhi):
2025-07-02 05:46:33.671 bj = b[j]
2025-07-02 05:46:33.682 cruncher.set_seq2(bj)
2025-07-02 05:46:33.692 for i in range(alo, ahi):
2025-07-02 05:46:33.704 ai = a[i]
2025-07-02 05:46:33.717 if ai == bj:
2025-07-02 05:46:33.727 if eqi is None:
2025-07-02 05:46:33.739 eqi, eqj = i, j
2025-07-02 05:46:33.748 continue
2025-07-02 05:46:33.757 cruncher.set_seq1(ai)
2025-07-02 05:46:33.770 # computing similarity is expensive, so use the quick
2025-07-02 05:46:33.780 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:33.787 # compares by a factor of 3.
2025-07-02 05:46:33.796 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:33.808 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:33.816 # of the computation is cached by cruncher
2025-07-02 05:46:33.823 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:33.831 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:33.837 cruncher.ratio() > best_ratio:
2025-07-02 05:46:33.849 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:33.859 if best_ratio < cutoff:
2025-07-02 05:46:33.869 # no non-identical "pretty close" pair
2025-07-02 05:46:33.877 if eqi is None:
2025-07-02 05:46:33.883 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:33.891 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:33.900 return
2025-07-02 05:46:33.912 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:33.921 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:33.928 else:
2025-07-02 05:46:33.940 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:33.948 eqi = None
2025-07-02 05:46:33.955
2025-07-02 05:46:33.963 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:33.973 # identical
2025-07-02 05:46:33.981
2025-07-02 05:46:33.988 # pump out diffs from before the synch point
2025-07-02 05:46:33.994 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:33.999
2025-07-02 05:46:34.006 # do intraline marking on the synch pair
2025-07-02 05:46:34.012 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:34.019 if eqi is None:
2025-07-02 05:46:34.030 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:34.039 atags = btags = ""
2025-07-02 05:46:34.046 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:34.054 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:34.059 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:34.066 if tag == 'replace':
2025-07-02 05:46:34.071 atags += '^' * la
2025-07-02 05:46:34.079 btags += '^' * lb
2025-07-02 05:46:34.091 elif tag == 'delete':
2025-07-02 05:46:34.099 atags += '-' * la
2025-07-02 05:46:34.107 elif tag == 'insert':
2025-07-02 05:46:34.114 btags += '+' * lb
2025-07-02 05:46:34.120 elif tag == 'equal':
2025-07-02 05:46:34.126 atags += ' ' * la
2025-07-02 05:46:34.132 btags += ' ' * lb
2025-07-02 05:46:34.139 else:
2025-07-02 05:46:34.147 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:34.158 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:34.166 else:
2025-07-02 05:46:34.174 # the synch pair is identical
2025-07-02 05:46:34.184 yield '  ' + aelt
2025-07-02 05:46:34.193
2025-07-02 05:46:34.201 # pump out diffs from after the synch point
2025-07-02 05:46:34.214 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:34.224
2025-07-02 05:46:34.231 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:34.238 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:34.250
2025-07-02 05:46:34.261 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:34.270 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:34.282 alo = 73, ahi = 1101
2025-07-02 05:46:34.297 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:34.308 blo = 73, bhi = 1101
2025-07-02 05:46:34.317
2025-07-02 05:46:34.325 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:34.336 g = []
2025-07-02 05:46:34.349 if alo < ahi:
2025-07-02 05:46:34.359 if blo < bhi:
2025-07-02 05:46:34.366 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:34.372 else:
2025-07-02 05:46:34.379 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:34.386 elif blo < bhi:
2025-07-02 05:46:34.395 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:34.406
2025-07-02 05:46:34.413 >       yield from g
2025-07-02 05:46:34.422
2025-07-02 05:46:34.430 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:34.444 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:34.454
2025-07-02 05:46:34.466 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:34.479 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:34.492 alo = 73, ahi = 1101
2025-07-02 05:46:34.505 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:34.516 blo = 73, bhi = 1101
2025-07-02 05:46:34.527
2025-07-02 05:46:34.538 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:34.544 r"""
2025-07-02 05:46:34.552 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:34.559 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:34.565 synch point, and intraline difference marking is done on the
2025-07-02 05:46:34.571 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:34.579
2025-07-02 05:46:34.586 Example:
2025-07-02 05:46:34.597
2025-07-02 05:46:34.610 >>> d = Differ()
2025-07-02 05:46:34.624 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:34.635 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:34.644 >>> print(''.join(results), end="")
2025-07-02 05:46:34.651 - abcDefghiJkl
2025-07-02 05:46:34.669 + abcdefGhijkl
2025-07-02 05:46:34.683 """
2025-07-02 05:46:34.689
2025-07-02 05:46:34.696 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:34.705 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:34.717 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:34.727 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:34.739 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:34.751
2025-07-02 05:46:34.761 # search for the pair that matches best without being identical
2025-07-02 05:46:34.771 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:34.786 # on junk -- unless we have to)
2025-07-02 05:46:34.799 for j in range(blo, bhi):
2025-07-02 05:46:34.808 bj = b[j]
2025-07-02 05:46:34.816 cruncher.set_seq2(bj)
2025-07-02 05:46:34.823 for i in range(alo, ahi):
2025-07-02 05:46:34.833 ai = a[i]
2025-07-02 05:46:34.840 if ai == bj:
2025-07-02 05:46:34.847 if eqi is None:
2025-07-02 05:46:34.854 eqi, eqj = i, j
2025-07-02 05:46:34.860 continue
2025-07-02 05:46:34.867 cruncher.set_seq1(ai)
2025-07-02 05:46:34.875 # computing similarity is expensive, so use the quick
2025-07-02 05:46:34.886 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:34.894 # compares by a factor of 3.
2025-07-02 05:46:34.902 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:34.910 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:34.916 # of the computation is cached by cruncher
2025-07-02 05:46:34.929 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:34.942 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:34.953 cruncher.ratio() > best_ratio:
2025-07-02 05:46:34.966 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:34.978 if best_ratio < cutoff:
2025-07-02 05:46:34.990 # no non-identical "pretty close" pair
2025-07-02 05:46:35.000 if eqi is None:
2025-07-02 05:46:35.010 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:35.018 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:35.025 return
2025-07-02 05:46:35.033 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:35.045 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:35.057 else:
2025-07-02 05:46:35.067 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:35.074 eqi = None
2025-07-02 05:46:35.081
2025-07-02 05:46:35.086 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:35.098 # identical
2025-07-02 05:46:35.108
2025-07-02 05:46:35.117 # pump out diffs from before the synch point
2025-07-02 05:46:35.129 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:35.142
2025-07-02 05:46:35.153 # do intraline marking on the synch pair
2025-07-02 05:46:35.159 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:35.171 if eqi is None:
2025-07-02 05:46:35.181 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:35.189 atags = btags = ""
2025-07-02 05:46:35.195 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:35.201 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:35.207 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:35.215 if tag == 'replace':
2025-07-02 05:46:35.224 atags += '^' * la
2025-07-02 05:46:35.231 btags += '^' * lb
2025-07-02 05:46:35.238 elif tag == 'delete':
2025-07-02 05:46:35.248 atags += '-' * la
2025-07-02 05:46:35.257 elif tag == 'insert':
2025-07-02 05:46:35.264 btags += '+' * lb
2025-07-02 05:46:35.269 elif tag == 'equal':
2025-07-02 05:46:35.273 atags += ' ' * la
2025-07-02 05:46:35.278 btags += ' ' * lb
2025-07-02 05:46:35.282 else:
2025-07-02 05:46:35.287 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:35.291 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:35.296 else:
2025-07-02 05:46:35.300 # the synch pair is identical
2025-07-02 05:46:35.305 yield '  ' + aelt
2025-07-02 05:46:35.310
2025-07-02 05:46:35.315 # pump out diffs from after the synch point
2025-07-02 05:46:35.325 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:35.335
2025-07-02 05:46:35.343 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:35.351 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:35.358
2025-07-02 05:46:35.364 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:35.372 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:35.378 alo = 74, ahi = 1101
2025-07-02 05:46:35.385 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:35.398 blo = 74, bhi = 1101
2025-07-02 05:46:35.409
2025-07-02 05:46:35.419 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:35.430 g = []
2025-07-02 05:46:35.439 if alo < ahi:
2025-07-02 05:46:35.447 if blo < bhi:
2025-07-02 05:46:35.454 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:35.466 else:
2025-07-02 05:46:35.476 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:35.484 elif blo < bhi:
2025-07-02 05:46:35.491 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:35.499
2025-07-02 05:46:35.510 >       yield from g
2025-07-02 05:46:35.518
2025-07-02 05:46:35.525 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:35.531 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:35.539
2025-07-02 05:46:35.550 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:35.560 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:35.566 alo = 74, ahi = 1101
2025-07-02 05:46:35.573 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:35.579 blo = 74, bhi = 1101
2025-07-02 05:46:35.585
2025-07-02 05:46:35.591 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:35.597 r"""
2025-07-02 05:46:35.609 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:35.618 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:35.626 synch point, and intraline difference marking is done on the
2025-07-02 05:46:35.639 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:35.647
2025-07-02 05:46:35.657 Example:
2025-07-02 05:46:35.670
2025-07-02 05:46:35.680 >>> d = Differ()
2025-07-02 05:46:35.689 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:35.696 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:35.704 >>> print(''.join(results), end="")
2025-07-02 05:46:35.710 - abcDefghiJkl
2025-07-02 05:46:35.730 + abcdefGhijkl
2025-07-02 05:46:35.746 """
2025-07-02 05:46:35.752
2025-07-02 05:46:35.760 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:35.768 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:35.775 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:35.783 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:35.791 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:35.797
2025-07-02 05:46:35.803 # search for the pair that matches best without being identical
2025-07-02 05:46:35.810 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:35.816 # on junk -- unless we have to)
2025-07-02 05:46:35.822 for j in range(blo, bhi):
2025-07-02 05:46:35.828 bj = b[j]
2025-07-02 05:46:35.833 cruncher.set_seq2(bj)
2025-07-02 05:46:35.841 for i in range(alo, ahi):
2025-07-02 05:46:35.850 ai = a[i]
2025-07-02 05:46:35.856 if ai == bj:
2025-07-02 05:46:35.863 if eqi is None:
2025-07-02 05:46:35.871 eqi, eqj = i, j
2025-07-02 05:46:35.877 continue
2025-07-02 05:46:35.885 cruncher.set_seq1(ai)
2025-07-02 05:46:35.892 # computing similarity is expensive, so use the quick
2025-07-02 05:46:35.898 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:35.905 # compares by a factor of 3.
2025-07-02 05:46:35.912 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:35.920 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:35.927 # of the computation is cached by cruncher
2025-07-02 05:46:35.935 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:35.941 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:35.952 cruncher.ratio() > best_ratio:
2025-07-02 05:46:35.963 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:35.975 if best_ratio < cutoff:
2025-07-02 05:46:35.986 # no non-identical "pretty close" pair
2025-07-02 05:46:35.997 if eqi is None:
2025-07-02 05:46:36.005 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:36.013 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:36.023 return
2025-07-02 05:46:36.037 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:36.054 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:36.063 else:
2025-07-02 05:46:36.071 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:36.078 eqi = None
2025-07-02 05:46:36.085
2025-07-02 05:46:36.090 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:36.095 # identical
2025-07-02 05:46:36.101
2025-07-02 05:46:36.106 # pump out diffs from before the synch point
2025-07-02 05:46:36.111 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:36.117
2025-07-02 05:46:36.123 # do intraline marking on the synch pair
2025-07-02 05:46:36.129 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:36.136 if eqi is None:
2025-07-02 05:46:36.142 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:36.149 atags = btags = ""
2025-07-02 05:46:36.157 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:36.173 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:36.178 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:36.185 if tag == 'replace':
2025-07-02 05:46:36.199 atags += '^' * la
2025-07-02 05:46:36.206 btags += '^' * lb
2025-07-02 05:46:36.212 elif tag == 'delete':
2025-07-02 05:46:36.219 atags += '-' * la
2025-07-02 05:46:36.226 elif tag == 'insert':
2025-07-02 05:46:36.238 btags += '+' * lb
2025-07-02 05:46:36.246 elif tag == 'equal':
2025-07-02 05:46:36.252 atags += ' ' * la
2025-07-02 05:46:36.258 btags += ' ' * lb
2025-07-02 05:46:36.263 else:
2025-07-02 05:46:36.268 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:36.273 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:36.279 else:
2025-07-02 05:46:36.285 # the synch pair is identical
2025-07-02 05:46:36.292 yield '  ' + aelt
2025-07-02 05:46:36.298
2025-07-02 05:46:36.308 # pump out diffs from after the synch point
2025-07-02 05:46:36.315 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:36.321
2025-07-02 05:46:36.327 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:36.333 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:36.338
2025-07-02 05:46:36.343 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:36.350 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:36.360 alo = 75, ahi = 1101
2025-07-02 05:46:36.370 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:36.378 blo = 75, bhi = 1101
2025-07-02 05:46:36.384
2025-07-02 05:46:36.390 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:36.402 g = []
2025-07-02 05:46:36.414 if alo < ahi:
2025-07-02 05:46:36.421 if blo < bhi:
2025-07-02 05:46:36.428 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:36.433 else:
2025-07-02 05:46:36.439 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:36.444 elif blo < bhi:
2025-07-02 05:46:36.450 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:36.460
2025-07-02 05:46:36.471 >       yield from g
2025-07-02 05:46:36.479
2025-07-02 05:46:36.487 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:36.497 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:36.508
2025-07-02 05:46:36.515 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:36.521 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:36.527 alo = 75, ahi = 1101
2025-07-02 05:46:36.539 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:36.549 blo = 75, bhi = 1101
2025-07-02 05:46:36.557
2025-07-02 05:46:36.565 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:36.574 r"""
2025-07-02 05:46:36.583 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:36.594 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:36.602 synch point, and intraline difference marking is done on the
2025-07-02 05:46:36.609 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:36.615
2025-07-02 05:46:36.623 Example:
2025-07-02 05:46:36.634
2025-07-02 05:46:36.643 >>> d = Differ()
2025-07-02 05:46:36.650 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:36.659 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:36.668 >>> print(''.join(results), end="")
2025-07-02 05:46:36.674 - abcDefghiJkl
2025-07-02 05:46:36.686 + abcdefGhijkl
2025-07-02 05:46:36.699 """
2025-07-02 05:46:36.709
2025-07-02 05:46:36.717 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:36.724 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:36.730 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:36.740 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:36.751 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:36.759
2025-07-02 05:46:36.768 # search for the pair that matches best without being identical
2025-07-02 05:46:36.775 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:36.782 # on junk -- unless we have to)
2025-07-02 05:46:36.790 for j in range(blo, bhi):
2025-07-02 05:46:36.798 bj = b[j]
2025-07-02 05:46:36.809 cruncher.set_seq2(bj)
2025-07-02 05:46:36.818 for i in range(alo, ahi):
2025-07-02 05:46:36.827 ai = a[i]
2025-07-02 05:46:36.836 if ai == bj:
2025-07-02 05:46:36.848 if eqi is None:
2025-07-02 05:46:36.858 eqi, eqj = i, j
2025-07-02 05:46:36.869 continue
2025-07-02 05:46:36.880 cruncher.set_seq1(ai)
2025-07-02 05:46:36.888 # computing similarity is expensive, so use the quick
2025-07-02 05:46:36.896 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:36.903 # compares by a factor of 3.
2025-07-02 05:46:36.911 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:36.922 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:36.930 # of the computation is cached by cruncher
2025-07-02 05:46:36.938 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:36.945 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:36.951 cruncher.ratio() > best_ratio:
2025-07-02 05:46:36.957 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:36.963 if best_ratio < cutoff:
2025-07-02 05:46:36.969 # no non-identical "pretty close" pair
2025-07-02 05:46:36.975 if eqi is None:
2025-07-02 05:46:36.983 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:36.993 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:37.001 return
2025-07-02 05:46:37.006 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:37.013 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:37.019 else:
2025-07-02 05:46:37.027 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:37.041 eqi = None
2025-07-02 05:46:37.051
2025-07-02 05:46:37.059 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:37.065 # identical
2025-07-02 05:46:37.070
2025-07-02 05:46:37.075 # pump out diffs from before the synch point
2025-07-02 05:46:37.081 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:37.091
2025-07-02 05:46:37.102 # do intraline marking on the synch pair
2025-07-02 05:46:37.111 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:37.118 if eqi is None:
2025-07-02 05:46:37.125 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:37.131 atags = btags = ""
2025-07-02 05:46:37.137 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:37.143 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:37.149 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:37.155 if tag == 'replace':
2025-07-02 05:46:37.162 atags += '^' * la
2025-07-02 05:46:37.168 btags += '^' * lb
2025-07-02 05:46:37.174 elif tag == 'delete':
2025-07-02 05:46:37.185 atags += '-' * la
2025-07-02 05:46:37.195 elif tag == 'insert':
2025-07-02 05:46:37.203 btags += '+' * lb
2025-07-02 05:46:37.211 elif tag == 'equal':
2025-07-02 05:46:37.217 atags += ' ' * la
2025-07-02 05:46:37.222 btags += ' ' * lb
2025-07-02 05:46:37.230 else:
2025-07-02 05:46:37.241 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:37.250 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:37.257 else:
2025-07-02 05:46:37.264 # the synch pair is identical
2025-07-02 05:46:37.270 yield '  ' + aelt
2025-07-02 05:46:37.276
2025-07-02 05:46:37.283 # pump out diffs from after the synch point
2025-07-02 05:46:37.290 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:37.296
2025-07-02 05:46:37.305 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:37.319 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:37.332
2025-07-02 05:46:37.343 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:37.353 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:37.361 alo = 76, ahi = 1101
2025-07-02 05:46:37.368 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:37.374 blo = 76, bhi = 1101
2025-07-02 05:46:37.385
2025-07-02 05:46:37.397 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:37.406 g = []
2025-07-02 05:46:37.413 if alo < ahi:
2025-07-02 05:46:37.419 if blo < bhi:
2025-07-02 05:46:37.426 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:37.431 else:
2025-07-02 05:46:37.437 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:37.445 elif blo < bhi:
2025-07-02 05:46:37.454 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:37.461
2025-07-02 05:46:37.467 >       yield from g
2025-07-02 05:46:37.473
2025-07-02 05:46:37.485 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:37.498 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:37.507
2025-07-02 05:46:37.515 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:37.522 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:37.527 alo = 76, ahi = 1101
2025-07-02 05:46:37.535 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:37.541 blo = 76, bhi = 1101
2025-07-02 05:46:37.546
2025-07-02 05:46:37.552 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:37.558 r"""
2025-07-02 05:46:37.568 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:37.579 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:37.587 synch point, and intraline difference marking is done on the
2025-07-02 05:46:37.593 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:37.599
2025-07-02 05:46:37.604 Example:
2025-07-02 05:46:37.611
2025-07-02 05:46:37.619 >>> d = Differ()
2025-07-02 05:46:37.626 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:37.632 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:37.637 >>> print(''.join(results), end="")
2025-07-02 05:46:37.646 - abcDefghiJkl
2025-07-02 05:46:37.668 + abcdefGhijkl
2025-07-02 05:46:37.684 """
2025-07-02 05:46:37.690
2025-07-02 05:46:37.697 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:37.703 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:37.710 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:37.723 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:37.733 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:37.744
2025-07-02 05:46:37.756 # search for the pair that matches best without being identical
2025-07-02 05:46:37.765 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:37.773 # on junk -- unless we have to)
2025-07-02 05:46:37.779 for j in range(blo, bhi):
2025-07-02 05:46:37.786 bj = b[j]
2025-07-02 05:46:37.796 cruncher.set_seq2(bj)
2025-07-02 05:46:37.806 for i in range(alo, ahi):
2025-07-02 05:46:37.814 ai = a[i]
2025-07-02 05:46:37.823 if ai == bj:
2025-07-02 05:46:37.830 if eqi is None:
2025-07-02 05:46:37.838 eqi, eqj = i, j
2025-07-02 05:46:37.846 continue
2025-07-02 05:46:37.854 cruncher.set_seq1(ai)
2025-07-02 05:46:37.861 # computing similarity is expensive, so use the quick
2025-07-02 05:46:37.868 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:37.875 # compares by a factor of 3.
2025-07-02 05:46:37.883 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:37.895 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:37.905 # of the computation is cached by cruncher
2025-07-02 05:46:37.912 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:37.920 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:37.926 cruncher.ratio() > best_ratio:
2025-07-02 05:46:37.933 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:37.940 if best_ratio < cutoff:
2025-07-02 05:46:37.950 # no non-identical "pretty close" pair
2025-07-02 05:46:37.961 if eqi is None:
2025-07-02 05:46:37.972 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:37.983 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:37.994 return
2025-07-02 05:46:38.007 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:38.018 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:38.027 else:
2025-07-02 05:46:38.041 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:38.051 eqi = None
2025-07-02 05:46:38.059
2025-07-02 05:46:38.071 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:38.080 # identical
2025-07-02 05:46:38.089
2025-07-02 05:46:38.096 # pump out diffs from before the synch point
2025-07-02 05:46:38.102 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:38.108
2025-07-02 05:46:38.115 # do intraline marking on the synch pair
2025-07-02 05:46:38.131 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:38.141 if eqi is None:
2025-07-02 05:46:38.147 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:38.152 atags = btags = ""
2025-07-02 05:46:38.157 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:38.162 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:38.167 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:38.172 if tag == 'replace':
2025-07-02 05:46:38.177 atags += '^' * la
2025-07-02 05:46:38.182 btags += '^' * lb
2025-07-02 05:46:38.187 elif tag == 'delete':
2025-07-02 05:46:38.192 atags += '-' * la
2025-07-02 05:46:38.197 elif tag == 'insert':
2025-07-02 05:46:38.203 btags += '+' * lb
2025-07-02 05:46:38.209 elif tag == 'equal':
2025-07-02 05:46:38.215 atags += ' ' * la
2025-07-02 05:46:38.222 btags += ' ' * lb
2025-07-02 05:46:38.235 else:
2025-07-02 05:46:38.244 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:38.253 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:38.259 else:
2025-07-02 05:46:38.265 # the synch pair is identical
2025-07-02 05:46:38.270 yield '  ' + aelt
2025-07-02 05:46:38.276
2025-07-02 05:46:38.282 # pump out diffs from after the synch point
2025-07-02 05:46:38.288 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:38.294
2025-07-02 05:46:38.300 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:38.306 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:38.312
2025-07-02 05:46:38.319 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:38.326 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:38.331 alo = 77, ahi = 1101
2025-07-02 05:46:38.339 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:38.345 blo = 77, bhi = 1101
2025-07-02 05:46:38.350
2025-07-02 05:46:38.361 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:38.370 g = []
2025-07-02 05:46:38.378 if alo < ahi:
2025-07-02 05:46:38.386 if blo < bhi:
2025-07-02 05:46:38.395 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:38.403 else:
2025-07-02 05:46:38.412 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:38.418 elif blo < bhi:
2025-07-02 05:46:38.425 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:38.430
2025-07-02 05:46:38.439 >       yield from g
2025-07-02 05:46:38.447
2025-07-02 05:46:38.454 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:38.462 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:38.472
2025-07-02 05:46:38.481 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:38.488 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:38.494 alo = 77, ahi = 1101
2025-07-02 05:46:38.506 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:38.517 blo = 77, bhi = 1101
2025-07-02 05:46:38.527
2025-07-02 05:46:38.535 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:38.543 r"""
2025-07-02 05:46:38.551 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:38.559 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:38.567 synch point, and intraline difference marking is done on the
2025-07-02 05:46:38.575 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:38.583
2025-07-02 05:46:38.594 Example:
2025-07-02 05:46:38.603
2025-07-02 05:46:38.610 >>> d = Differ()
2025-07-02 05:46:38.618 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:38.623 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:38.629 >>> print(''.join(results), end="")
2025-07-02 05:46:38.634 - abcDefghiJkl
2025-07-02 05:46:38.643 + abcdefGhijkl
2025-07-02 05:46:38.655 """
2025-07-02 05:46:38.661
2025-07-02 05:46:38.669 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:38.678 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:38.688 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:38.697 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:38.705 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:38.711
2025-07-02 05:46:38.718 # search for the pair that matches best without being identical
2025-07-02 05:46:38.725 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:38.731 # on junk -- unless we have to)
2025-07-02 05:46:38.736 for j in range(blo, bhi):
2025-07-02 05:46:38.741 bj = b[j]
2025-07-02 05:46:38.746 cruncher.set_seq2(bj)
2025-07-02 05:46:38.751 for i in range(alo, ahi):
2025-07-02 05:46:38.756 ai = a[i]
2025-07-02 05:46:38.761 if ai == bj:
2025-07-02 05:46:38.766 if eqi is None:
2025-07-02 05:46:38.775 eqi, eqj = i, j
2025-07-02 05:46:38.784 continue
2025-07-02 05:46:38.792 cruncher.set_seq1(ai)
2025-07-02 05:46:38.799 # computing similarity is expensive, so use the quick
2025-07-02 05:46:38.806 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:38.817 # compares by a factor of 3.
2025-07-02 05:46:38.826 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:38.834 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:38.843 # of the computation is cached by cruncher
2025-07-02 05:46:38.850 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:38.858 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:38.870 cruncher.ratio() > best_ratio:
2025-07-02 05:46:38.880 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:38.889 if best_ratio < cutoff:
2025-07-02 05:46:38.897 # no non-identical "pretty close" pair
2025-07-02 05:46:38.903 if eqi is None:
2025-07-02 05:46:38.908 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:38.915 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:38.923 return
2025-07-02 05:46:38.935 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:38.943 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:38.953 else:
2025-07-02 05:46:38.966 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:38.977 eqi = None
2025-07-02 05:46:38.990
2025-07-02 05:46:39.002 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:39.013 # identical
2025-07-02 05:46:39.022
2025-07-02 05:46:39.030 # pump out diffs from before the synch point
2025-07-02 05:46:39.037 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:39.043
2025-07-02 05:46:39.048 # do intraline marking on the synch pair
2025-07-02 05:46:39.055 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:39.063 if eqi is None:
2025-07-02 05:46:39.071 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:39.081 atags = btags = ""
2025-07-02 05:46:39.093 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:39.104 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:39.115 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:39.124 if tag == 'replace':
2025-07-02 05:46:39.133 atags += '^' * la
2025-07-02 05:46:39.141 btags += '^' * lb
2025-07-02 05:46:39.148 elif tag == 'delete':
2025-07-02 05:46:39.160 atags += '-' * la
2025-07-02 05:46:39.169 elif tag == 'insert':
2025-07-02 05:46:39.177 btags += '+' * lb
2025-07-02 05:46:39.183 elif tag == 'equal':
2025-07-02 05:46:39.190 atags += ' ' * la
2025-07-02 05:46:39.195 btags += ' ' * lb
2025-07-02 05:46:39.201 else:
2025-07-02 05:46:39.213 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:39.222 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:39.230 else:
2025-07-02 05:46:39.237 # the synch pair is identical
2025-07-02 05:46:39.243 yield '  ' + aelt
2025-07-02 05:46:39.248
2025-07-02 05:46:39.254 # pump out diffs from after the synch point
2025-07-02 05:46:39.261 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:39.267
2025-07-02 05:46:39.273 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:39.280 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:39.287
2025-07-02 05:46:39.295 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:39.302 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:39.309 alo = 78, ahi = 1101
2025-07-02 05:46:39.317 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:39.324 blo = 78, bhi = 1101
2025-07-02 05:46:39.330
2025-07-02 05:46:39.336 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:39.342 g = []
2025-07-02 05:46:39.350 if alo < ahi:
2025-07-02 05:46:39.357 if blo < bhi:
2025-07-02 05:46:39.364 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:39.370 else:
2025-07-02 05:46:39.382 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:39.390 elif blo < bhi:
2025-07-02 05:46:39.396 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:39.401
2025-07-02 05:46:39.406 >       yield from g
2025-07-02 05:46:39.413
2025-07-02 05:46:39.420 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:39.426 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:39.433
2025-07-02 05:46:39.440 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:39.449 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:39.456 alo = 78, ahi = 1101
2025-07-02 05:46:39.469 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:39.478 blo = 78, bhi = 1101
2025-07-02 05:46:39.486
2025-07-02 05:46:39.493 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:39.500 r"""
2025-07-02 05:46:39.508 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:39.515 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:39.523 synch point, and intraline difference marking is done on the
2025-07-02 05:46:39.530 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:39.542
2025-07-02 05:46:39.552 Example:
2025-07-02 05:46:39.560
2025-07-02 05:46:39.568 >>> d = Differ()
2025-07-02 05:46:39.574 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:39.581 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:39.587 >>> print(''.join(results), end="")
2025-07-02 05:46:39.595 - abcDefghiJkl
2025-07-02 05:46:39.612 + abcdefGhijkl
2025-07-02 05:46:39.632 """
2025-07-02 05:46:39.644
2025-07-02 05:46:39.655 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:39.664 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:39.671 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:39.679 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:39.690 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:39.698
2025-07-02 05:46:39.706 # search for the pair that matches best without being identical
2025-07-02 05:46:39.715 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:39.722 # on junk -- unless we have to)
2025-07-02 05:46:39.731 for j in range(blo, bhi):
2025-07-02 05:46:39.741 bj = b[j]
2025-07-02 05:46:39.750 cruncher.set_seq2(bj)
2025-07-02 05:46:39.759 for i in range(alo, ahi):
2025-07-02 05:46:39.766 ai = a[i]
2025-07-02 05:46:39.777 if ai == bj:
2025-07-02 05:46:39.786 if eqi is None:
2025-07-02 05:46:39.794 eqi, eqj = i, j
2025-07-02 05:46:39.801 continue
2025-07-02 05:46:39.807 cruncher.set_seq1(ai)
2025-07-02 05:46:39.813 # computing similarity is expensive, so use the quick
2025-07-02 05:46:39.822 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:39.833 # compares by a factor of 3.
2025-07-02 05:46:39.843 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:39.855 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:39.865 # of the computation is cached by cruncher
2025-07-02 05:46:39.877 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:39.887 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:39.900 cruncher.ratio() > best_ratio:
2025-07-02 05:46:39.915 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:39.926 if best_ratio < cutoff:
2025-07-02 05:46:39.935 # no non-identical "pretty close" pair
2025-07-02 05:46:39.942 if eqi is None:
2025-07-02 05:46:39.953 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:39.964 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:39.972 return
2025-07-02 05:46:39.979 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:39.986 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:39.992 else:
2025-07-02 05:46:39.998 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:40.008 eqi = None
2025-07-02 05:46:40.016
2025-07-02 05:46:40.027 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:40.040 # identical
2025-07-02 05:46:40.049
2025-07-02 05:46:40.056 # pump out diffs from before the synch point
2025-07-02 05:46:40.062 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:40.072
2025-07-02 05:46:40.083 # do intraline marking on the synch pair
2025-07-02 05:46:40.090 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:40.096 if eqi is None:
2025-07-02 05:46:40.101 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:40.106 atags = btags = ""
2025-07-02 05:46:40.116 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:40.127 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:40.138 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:40.147 if tag == 'replace':
2025-07-02 05:46:40.158 atags += '^' * la
2025-07-02 05:46:40.166 btags += '^' * lb
2025-07-02 05:46:40.175 elif tag == 'delete':
2025-07-02 05:46:40.186 atags += '-' * la
2025-07-02 05:46:40.195 elif tag == 'insert':
2025-07-02 05:46:40.206 btags += '+' * lb
2025-07-02 05:46:40.218 elif tag == 'equal':
2025-07-02 05:46:40.227 atags += ' ' * la
2025-07-02 05:46:40.235 btags += ' ' * lb
2025-07-02 05:46:40.241 else:
2025-07-02 05:46:40.247 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:40.253 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:40.259 else:
2025-07-02 05:46:40.265 # the synch pair is identical
2025-07-02 05:46:40.271 yield '  ' + aelt
2025-07-02 05:46:40.277
2025-07-02 05:46:40.283 # pump out diffs from after the synch point
2025-07-02 05:46:40.289 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:40.294
2025-07-02 05:46:40.301 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:40.307 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:40.313
2025-07-02 05:46:40.319 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:40.327 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:40.339 alo = 79, ahi = 1101
2025-07-02 05:46:40.353 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:40.361 blo = 79, bhi = 1101
2025-07-02 05:46:40.368
2025-07-02 05:46:40.375 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:40.382 g = []
2025-07-02 05:46:40.388 if alo < ahi:
2025-07-02 05:46:40.395 if blo < bhi:
2025-07-02 05:46:40.403 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:40.409 else:
2025-07-02 05:46:40.414 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:40.421 elif blo < bhi:
2025-07-02 05:46:40.428 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:40.434
2025-07-02 05:46:40.440 >       yield from g
2025-07-02 05:46:40.445
2025-07-02 05:46:40.454 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:40.460 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:40.465
2025-07-02 05:46:40.471 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:40.476 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:40.482 alo = 79, ahi = 1101
2025-07-02 05:46:40.489 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:40.495 blo = 79, bhi = 1101
2025-07-02 05:46:40.502
2025-07-02 05:46:40.511 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:40.521 r"""
2025-07-02 05:46:40.532 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:40.541 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:40.549 synch point, and intraline difference marking is done on the
2025-07-02 05:46:40.561 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:40.574
2025-07-02 05:46:40.586 Example:
2025-07-02 05:46:40.600
2025-07-02 05:46:40.612 >>> d = Differ()
2025-07-02 05:46:40.622 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:40.630 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:40.638 >>> print(''.join(results), end="")
2025-07-02 05:46:40.645 - abcDefghiJkl
2025-07-02 05:46:40.661 + abcdefGhijkl
2025-07-02 05:46:40.682 """
2025-07-02 05:46:40.693
2025-07-02 05:46:40.705 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:40.715 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:40.723 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:40.730 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:40.739 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:40.751
2025-07-02 05:46:40.759 # search for the pair that matches best without being identical
2025-07-02 05:46:40.766 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:40.772 # on junk -- unless we have to)
2025-07-02 05:46:40.778 for j in range(blo, bhi):
2025-07-02 05:46:40.784 bj = b[j]
2025-07-02 05:46:40.790 cruncher.set_seq2(bj)
2025-07-02 05:46:40.800 for i in range(alo, ahi):
2025-07-02 05:46:40.810 ai = a[i]
2025-07-02 05:46:40.819 if ai == bj:
2025-07-02 05:46:40.827 if eqi is None:
2025-07-02 05:46:40.839 eqi, eqj = i, j
2025-07-02 05:46:40.847 continue
2025-07-02 05:46:40.855 cruncher.set_seq1(ai)
2025-07-02 05:46:40.864 # computing similarity is expensive, so use the quick
2025-07-02 05:46:40.872 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:40.878 # compares by a factor of 3.
2025-07-02 05:46:40.885 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:40.891 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:40.897 # of the computation is cached by cruncher
2025-07-02 05:46:40.908 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:40.915 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:40.926 cruncher.ratio() > best_ratio:
2025-07-02 05:46:40.933 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:40.939 if best_ratio < cutoff:
2025-07-02 05:46:40.950 # no non-identical "pretty close" pair
2025-07-02 05:46:40.959 if eqi is None:
2025-07-02 05:46:40.967 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:40.974 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:40.979 return
2025-07-02 05:46:40.984 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:40.988 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:40.993 else:
2025-07-02 05:46:40.998 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:41.002 eqi = None
2025-07-02 05:46:41.007
2025-07-02 05:46:41.011 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:41.016 # identical
2025-07-02 05:46:41.020
2025-07-02 05:46:41.025 # pump out diffs from before the synch point
2025-07-02 05:46:41.030 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:41.034
2025-07-02 05:46:41.039 # do intraline marking on the synch pair
2025-07-02 05:46:41.043 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:41.048 if eqi is None:
2025-07-02 05:46:41.052 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:41.057 atags = btags = ""
2025-07-02 05:46:41.061 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:41.066 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:41.070 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:41.075 if tag == 'replace':
2025-07-02 05:46:41.083 atags += '^' * la
2025-07-02 05:46:41.091 btags += '^' * lb
2025-07-02 05:46:41.098 elif tag == 'delete':
2025-07-02 05:46:41.106 atags += '-' * la
2025-07-02 05:46:41.114 elif tag == 'insert':
2025-07-02 05:46:41.122 btags += '+' * lb
2025-07-02 05:46:41.130 elif tag == 'equal':
2025-07-02 05:46:41.141 atags += ' ' * la
2025-07-02 05:46:41.154 btags += ' ' * lb
2025-07-02 05:46:41.166 else:
2025-07-02 05:46:41.180 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:41.192 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:41.200 else:
2025-07-02 05:46:41.208 # the synch pair is identical
2025-07-02 05:46:41.215 yield '  ' + aelt
2025-07-02 05:46:41.221
2025-07-02 05:46:41.227 # pump out diffs from after the synch point
2025-07-02 05:46:41.234 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:41.241
2025-07-02 05:46:41.247 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:41.256 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:41.267
2025-07-02 05:46:41.279 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:41.288 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:41.294 alo = 80, ahi = 1101
2025-07-02 05:46:41.301 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:41.307 blo = 80, bhi = 1101
2025-07-02 05:46:41.314
2025-07-02 05:46:41.324 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:41.334 g = []
2025-07-02 05:46:41.339 if alo < ahi:
2025-07-02 05:46:41.344 if blo < bhi:
2025-07-02 05:46:41.350 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:41.355 else:
2025-07-02 05:46:41.360 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:41.366 elif blo < bhi:
2025-07-02 05:46:41.371 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:41.377
2025-07-02 05:46:41.384 >       yield from g
2025-07-02 05:46:41.391
2025-07-02 05:46:41.397 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:41.403 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:41.408
2025-07-02 05:46:41.416 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:41.424 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:41.431 alo = 80, ahi = 1101
2025-07-02 05:46:41.443 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:41.450 blo = 80, bhi = 1101
2025-07-02 05:46:41.457
2025-07-02 05:46:41.470 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:41.484 r"""
2025-07-02 05:46:41.496 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:41.505 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:41.511 synch point, and intraline difference marking is done on the
2025-07-02 05:46:41.518 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:41.524
2025-07-02 05:46:41.530 Example:
2025-07-02 05:46:41.544
2025-07-02 05:46:41.553 >>> d = Differ()
2025-07-02 05:46:41.564 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:41.575 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:41.584 >>> print(''.join(results), end="")
2025-07-02 05:46:41.597 - abcDefghiJkl
2025-07-02 05:46:41.619 + abcdefGhijkl
2025-07-02 05:46:41.636 """
2025-07-02 05:46:41.643
2025-07-02 05:46:41.649 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:41.654 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:41.661 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:41.667 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:41.672 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:41.678
2025-07-02 05:46:41.683 # search for the pair that matches best without being identical
2025-07-02 05:46:41.691 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:41.702 # on junk -- unless we have to)
2025-07-02 05:46:41.709 for j in range(blo, bhi):
2025-07-02 05:46:41.716 bj = b[j]
2025-07-02 05:46:41.723 cruncher.set_seq2(bj)
2025-07-02 05:46:41.729 for i in range(alo, ahi):
2025-07-02 05:46:41.734 ai = a[i]
2025-07-02 05:46:41.740 if ai == bj:
2025-07-02 05:46:41.745 if eqi is None:
2025-07-02 05:46:41.750 eqi, eqj = i, j
2025-07-02 05:46:41.763 continue
2025-07-02 05:46:41.776 cruncher.set_seq1(ai)
2025-07-02 05:46:41.786 # computing similarity is expensive, so use the quick
2025-07-02 05:46:41.795 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:41.801 # compares by a factor of 3.
2025-07-02 05:46:41.806 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:41.812 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:41.819 # of the computation is cached by cruncher
2025-07-02 05:46:41.832 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:41.840 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:41.848 cruncher.ratio() > best_ratio:
2025-07-02 05:46:41.856 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:41.862 if best_ratio < cutoff:
2025-07-02 05:46:41.868 # no non-identical "pretty close" pair
2025-07-02 05:46:41.874 if eqi is None:
2025-07-02 05:46:41.879 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:41.884 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:41.890 return
2025-07-02 05:46:41.898 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:41.907 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:41.919 else:
2025-07-02 05:46:41.929 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:41.944 eqi = None
2025-07-02 05:46:41.954
2025-07-02 05:46:41.963 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:41.970 # identical
2025-07-02 05:46:41.975
2025-07-02 05:46:41.983 # pump out diffs from before the synch point
2025-07-02 05:46:41.996 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:42.004
2025-07-02 05:46:42.013 # do intraline marking on the synch pair
2025-07-02 05:46:42.027 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:42.036 if eqi is None:
2025-07-02 05:46:42.043 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:42.049 atags = btags = ""
2025-07-02 05:46:42.064 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:42.076 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:42.083 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:42.093 if tag == 'replace':
2025-07-02 05:46:42.104 atags += '^' * la
2025-07-02 05:46:42.113 btags += '^' * lb
2025-07-02 05:46:42.122 elif tag == 'delete':
2025-07-02 05:46:42.129 atags += '-' * la
2025-07-02 05:46:42.136 elif tag == 'insert':
2025-07-02 05:46:42.143 btags += '+' * lb
2025-07-02 05:46:42.149 elif tag == 'equal':
2025-07-02 05:46:42.155 atags += ' ' * la
2025-07-02 05:46:42.162 btags += ' ' * lb
2025-07-02 05:46:42.168 else:
2025-07-02 05:46:42.175 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:42.182 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:42.188 else:
2025-07-02 05:46:42.194 # the synch pair is identical
2025-07-02 05:46:42.198 yield '  ' + aelt
2025-07-02 05:46:42.205
2025-07-02 05:46:42.210 # pump out diffs from after the synch point
2025-07-02 05:46:42.215 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:42.219
2025-07-02 05:46:42.225 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:42.230 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:42.236
2025-07-02 05:46:42.242 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:42.252 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:42.259 alo = 81, ahi = 1101
2025-07-02 05:46:42.267 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:42.275 blo = 81, bhi = 1101
2025-07-02 05:46:42.287
2025-07-02 05:46:42.295 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:42.303 g = []
2025-07-02 05:46:42.312 if alo < ahi:
2025-07-02 05:46:42.320 if blo < bhi:
2025-07-02 05:46:42.328 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:42.334 else:
2025-07-02 05:46:42.345 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:42.356 elif blo < bhi:
2025-07-02 05:46:42.363 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:42.370
2025-07-02 05:46:42.376 >       yield from g
2025-07-02 05:46:42.385
2025-07-02 05:46:42.396 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:42.408 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:42.418
2025-07-02 05:46:42.427 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:42.439 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:42.448 alo = 81, ahi = 1101
2025-07-02 05:46:42.456 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:42.463 blo = 81, bhi = 1101
2025-07-02 05:46:42.469
2025-07-02 05:46:42.475 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:42.481 r"""
2025-07-02 05:46:42.493 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:42.506 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:42.518 synch point, and intraline difference marking is done on the
2025-07-02 05:46:42.527 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:42.540
2025-07-02 05:46:42.549 Example:
2025-07-02 05:46:42.556
2025-07-02 05:46:42.562 >>> d = Differ()
2025-07-02 05:46:42.567 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:42.572 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:42.578 >>> print(''.join(results), end="")
2025-07-02 05:46:42.589 - abcDefghiJkl
2025-07-02 05:46:42.606 + abcdefGhijkl
2025-07-02 05:46:42.620 """
2025-07-02 05:46:42.626
2025-07-02 05:46:42.636 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:42.647 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:42.655 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:42.665 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:42.676 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:42.689
2025-07-02 05:46:42.698 # search for the pair that matches best without being identical
2025-07-02 05:46:42.705 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:42.712 # on junk -- unless we have to)
2025-07-02 05:46:42.719 for j in range(blo, bhi):
2025-07-02 05:46:42.727 bj = b[j]
2025-07-02 05:46:42.738 cruncher.set_seq2(bj)
2025-07-02 05:46:42.747 for i in range(alo, ahi):
2025-07-02 05:46:42.755 ai = a[i]
2025-07-02 05:46:42.763 if ai == bj:
2025-07-02 05:46:42.775 if eqi is None:
2025-07-02 05:46:42.784 eqi, eqj = i, j
2025-07-02 05:46:42.796 continue
2025-07-02 05:46:42.808 cruncher.set_seq1(ai)
2025-07-02 05:46:42.816 # computing similarity is expensive, so use the quick
2025-07-02 05:46:42.831 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:42.844 # compares by a factor of 3.
2025-07-02 05:46:42.853 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:42.864 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:42.876 # of the computation is cached by cruncher
2025-07-02 05:46:42.884 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:42.894 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:42.906 cruncher.ratio() > best_ratio:
2025-07-02 05:46:42.917 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:42.930 if best_ratio < cutoff:
2025-07-02 05:46:42.942 # no non-identical "pretty close" pair
2025-07-02 05:46:42.955 if eqi is None:
2025-07-02 05:46:42.966 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:42.974 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:42.981 return
2025-07-02 05:46:42.987 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:42.995 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:43.008 else:
2025-07-02 05:46:43.022 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:43.034 eqi = None
2025-07-02 05:46:43.044
2025-07-02 05:46:43.052 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:43.061 # identical
2025-07-02 05:46:43.068
2025-07-02 05:46:43.074 # pump out diffs from before the synch point
2025-07-02 05:46:43.081 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:43.086
2025-07-02 05:46:43.098 # do intraline marking on the synch pair
2025-07-02 05:46:43.107 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:43.113 if eqi is None:
2025-07-02 05:46:43.119 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:43.126 atags = btags = ""
2025-07-02 05:46:43.133 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:43.140 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:43.146 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:43.155 if tag == 'replace':
2025-07-02 05:46:43.163 atags += '^' * la
2025-07-02 05:46:43.169 btags += '^' * lb
2025-07-02 05:46:43.179 elif tag == 'delete':
2025-07-02 05:46:43.186 atags += '-' * la
2025-07-02 05:46:43.193 elif tag == 'insert':
2025-07-02 05:46:43.202 btags += '+' * lb
2025-07-02 05:46:43.214 elif tag == 'equal':
2025-07-02 05:46:43.227 atags += ' ' * la
2025-07-02 05:46:43.240 btags += ' ' * lb
2025-07-02 05:46:43.248 else:
2025-07-02 05:46:43.257 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:43.269 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:43.279 else:
2025-07-02 05:46:43.286 # the synch pair is identical
2025-07-02 05:46:43.295 yield '  ' + aelt
2025-07-02 05:46:43.306
2025-07-02 05:46:43.314 # pump out diffs from after the synch point
2025-07-02 05:46:43.322 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:43.330
2025-07-02 05:46:43.345 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:43.355 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:43.367
2025-07-02 05:46:43.378 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:43.388 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:43.396 alo = 82, ahi = 1101
2025-07-02 05:46:43.404 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:43.411 blo = 82, bhi = 1101
2025-07-02 05:46:43.416
2025-07-02 05:46:43.422 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:43.429 g = []
2025-07-02 05:46:43.434 if alo < ahi:
2025-07-02 05:46:43.445 if blo < bhi:
2025-07-02 05:46:43.456 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:43.467 else:
2025-07-02 05:46:43.477 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:43.485 elif blo < bhi:
2025-07-02 05:46:43.498 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:43.508
2025-07-02 05:46:43.516 >       yield from g
2025-07-02 05:46:43.522
2025-07-02 05:46:43.533 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:43.544 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:43.552
2025-07-02 05:46:43.559 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:43.569 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:43.581 alo = 82, ahi = 1101
2025-07-02 05:46:43.592 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:43.599 blo = 82, bhi = 1101
2025-07-02 05:46:43.606
2025-07-02 05:46:43.613 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:43.619 r"""
2025-07-02 05:46:43.625 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:43.636 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:43.646 synch point, and intraline difference marking is done on the
2025-07-02 05:46:43.659 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:43.671
2025-07-02 05:46:43.681 Example:
2025-07-02 05:46:43.693
2025-07-02 05:46:43.704 >>> d = Differ()
2025-07-02 05:46:43.717 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:43.727 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:43.735 >>> print(''.join(results), end="")
2025-07-02 05:46:43.742 - abcDefghiJkl
2025-07-02 05:46:43.768 + abcdefGhijkl
2025-07-02 05:46:43.792 """
2025-07-02 05:46:43.802
2025-07-02 05:46:43.811 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:43.819 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:43.826 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:43.832 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:43.838 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:43.845
2025-07-02 05:46:43.857 # search for the pair that matches best without being identical
2025-07-02 05:46:43.868 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:43.876 # on junk -- unless we have to)
2025-07-02 05:46:43.883 for j in range(blo, bhi):
2025-07-02 05:46:43.888 bj = b[j]
2025-07-02 05:46:43.895 cruncher.set_seq2(bj)
2025-07-02 05:46:43.902 for i in range(alo, ahi):
2025-07-02 05:46:43.910 ai = a[i]
2025-07-02 05:46:43.921 if ai == bj:
2025-07-02 05:46:43.930 if eqi is None:
2025-07-02 05:46:43.938 eqi, eqj = i, j
2025-07-02 05:46:43.946 continue
2025-07-02 05:46:43.954 cruncher.set_seq1(ai)
2025-07-02 05:46:43.969 # computing similarity is expensive, so use the quick
2025-07-02 05:46:43.981 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:43.993 # compares by a factor of 3.
2025-07-02 05:46:44.005 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:44.016 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:44.027 # of the computation is cached by cruncher
2025-07-02 05:46:44.040 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:44.050 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:44.059 cruncher.ratio() > best_ratio:
2025-07-02 05:46:44.072 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:44.081 if best_ratio < cutoff:
2025-07-02 05:46:44.088 # no non-identical "pretty close" pair
2025-07-02 05:46:44.094 if eqi is None:
2025-07-02 05:46:44.100 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:44.107 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:44.114 return
2025-07-02 05:46:44.125 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:44.136 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:44.144 else:
2025-07-02 05:46:44.151 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:44.158 eqi = None
2025-07-02 05:46:44.169
2025-07-02 05:46:44.180 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:44.189 # identical
2025-07-02 05:46:44.196
2025-07-02 05:46:44.204 # pump out diffs from before the synch point
2025-07-02 05:46:44.211 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:44.219
2025-07-02 05:46:44.232 # do intraline marking on the synch pair
2025-07-02 05:46:44.243 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:44.256 if eqi is None:
2025-07-02 05:46:44.270 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:44.280 atags = btags = ""
2025-07-02 05:46:44.288 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:44.295 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:44.302 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:44.308 if tag == 'replace':
2025-07-02 05:46:44.315 atags += '^' * la
2025-07-02 05:46:44.321 btags += '^' * lb
2025-07-02 05:46:44.329 elif tag == 'delete':
2025-07-02 05:46:44.338 atags += '-' * la
2025-07-02 05:46:44.347 elif tag == 'insert':
2025-07-02 05:46:44.361 btags += '+' * lb
2025-07-02 05:46:44.373 elif tag == 'equal':
2025-07-02 05:46:44.386 atags += ' ' * la
2025-07-02 05:46:44.395 btags += ' ' * lb
2025-07-02 05:46:44.403 else:
2025-07-02 05:46:44.416 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:44.425 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:44.431 else:
2025-07-02 05:46:44.438 # the synch pair is identical
2025-07-02 05:46:44.449 yield '  ' + aelt
2025-07-02 05:46:44.459
2025-07-02 05:46:44.465 # pump out diffs from after the synch point
2025-07-02 05:46:44.471 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:44.477
2025-07-02 05:46:44.482 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:44.488 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:44.492
2025-07-02 05:46:44.497 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:44.504 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:44.509 alo = 83, ahi = 1101
2025-07-02 05:46:44.515 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:44.520 blo = 83, bhi = 1101
2025-07-02 05:46:44.526
2025-07-02 05:46:44.539 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:44.549 g = []
2025-07-02 05:46:44.558 if alo < ahi:
2025-07-02 05:46:44.567 if blo < bhi:
2025-07-02 05:46:44.577 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:44.588 else:
2025-07-02 05:46:44.599 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:44.611 elif blo < bhi:
2025-07-02 05:46:44.623 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:44.636
2025-07-02 05:46:44.647 >       yield from g
2025-07-02 05:46:44.661
2025-07-02 05:46:44.675 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:44.686 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:44.695
2025-07-02 05:46:44.704 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:44.719 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:44.731 alo = 83, ahi = 1101
2025-07-02 05:46:44.739 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:44.747 blo = 83, bhi = 1101
2025-07-02 05:46:44.753
2025-07-02 05:46:44.760 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:44.765 r"""
2025-07-02 05:46:44.769 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:44.776 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:44.782 synch point, and intraline difference marking is done on the
2025-07-02 05:46:44.790 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:44.797
2025-07-02 05:46:44.804 Example:
2025-07-02 05:46:44.810
2025-07-02 05:46:44.821 >>> d = Differ()
2025-07-02 05:46:44.832 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:44.839 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:44.846 >>> print(''.join(results), end="")
2025-07-02 05:46:44.850 - abcDefghiJkl
2025-07-02 05:46:44.870 + abcdefGhijkl
2025-07-02 05:46:44.887 """
2025-07-02 05:46:44.895
2025-07-02 05:46:44.902 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:44.909 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:44.914 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:44.918 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:44.924 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:44.930
2025-07-02 05:46:44.936 # search for the pair that matches best without being identical
2025-07-02 05:46:44.943 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:44.950 # on junk -- unless we have to)
2025-07-02 05:46:44.957 for j in range(blo, bhi):
2025-07-02 05:46:44.964 bj = b[j]
2025-07-02 05:46:44.971 cruncher.set_seq2(bj)
2025-07-02 05:46:44.981 for i in range(alo, ahi):
2025-07-02 05:46:44.993 ai = a[i]
2025-07-02 05:46:45.003 if ai == bj:
2025-07-02 05:46:45.017 if eqi is None:
2025-07-02 05:46:45.027 eqi, eqj = i, j
2025-07-02 05:46:45.040 continue
2025-07-02 05:46:45.051 cruncher.set_seq1(ai)
2025-07-02 05:46:45.062 # computing similarity is expensive, so use the quick
2025-07-02 05:46:45.073 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:45.085 # compares by a factor of 3.
2025-07-02 05:46:45.098 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:45.106 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:45.115 # of the computation is cached by cruncher
2025-07-02 05:46:45.125 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:45.133 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:45.140 cruncher.ratio() > best_ratio:
2025-07-02 05:46:45.147 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:45.153 if best_ratio < cutoff:
2025-07-02 05:46:45.159 # no non-identical "pretty close" pair
2025-07-02 05:46:45.167 if eqi is None:
2025-07-02 05:46:45.177 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:45.186 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:45.195 return
2025-07-02 05:46:45.204 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:45.211 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:45.219 else:
2025-07-02 05:46:45.230 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:45.240 eqi = None
2025-07-02 05:46:45.253
2025-07-02 05:46:45.267 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:45.282 # identical
2025-07-02 05:46:45.292
2025-07-02 05:46:45.299 # pump out diffs from before the synch point
2025-07-02 05:46:45.305 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:45.311
2025-07-02 05:46:45.317 # do intraline marking on the synch pair
2025-07-02 05:46:45.324 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:45.333 if eqi is None:
2025-07-02 05:46:45.343 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:45.354 atags = btags = ""
2025-07-02 05:46:45.363 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:45.371 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:45.380 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:45.391 if tag == 'replace':
2025-07-02 05:46:45.400 atags += '^' * la
2025-07-02 05:46:45.408 btags += '^' * lb
2025-07-02 05:46:45.415 elif tag == 'delete':
2025-07-02 05:46:45.423 atags += '-' * la
2025-07-02 05:46:45.434 elif tag == 'insert':
2025-07-02 05:46:45.443 btags += '+' * lb
2025-07-02 05:46:45.449 elif tag == 'equal':
2025-07-02 05:46:45.457 atags += ' ' * la
2025-07-02 05:46:45.462 btags += ' ' * lb
2025-07-02 05:46:45.467 else:
2025-07-02 05:46:45.472 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:45.478 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:45.488 else:
2025-07-02 05:46:45.498 # the synch pair is identical
2025-07-02 05:46:45.505 yield '  ' + aelt
2025-07-02 05:46:45.513
2025-07-02 05:46:45.519 # pump out diffs from after the synch point
2025-07-02 05:46:45.527 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:45.535
2025-07-02 05:46:45.547 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:45.559 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:45.571
2025-07-02 05:46:45.580 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:45.593 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:45.606 alo = 84, ahi = 1101
2025-07-02 05:46:45.616 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:45.628 blo = 84, bhi = 1101
2025-07-02 05:46:45.638
2025-07-02 05:46:45.646 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:45.658 g = []
2025-07-02 05:46:45.670 if alo < ahi:
2025-07-02 05:46:45.681 if blo < bhi:
2025-07-02 05:46:45.691 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:45.699 else:
2025-07-02 05:46:45.706 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:45.718 elif blo < bhi:
2025-07-02 05:46:45.728 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:45.741
2025-07-02 05:46:45.756 >       yield from g
2025-07-02 05:46:45.767
2025-07-02 05:46:45.777 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:45.789 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:45.802
2025-07-02 05:46:45.813 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:45.823 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:45.830 alo = 84, ahi = 1101
2025-07-02 05:46:45.838 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:45.844 blo = 84, bhi = 1101
2025-07-02 05:46:45.851
2025-07-02 05:46:45.861 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:45.869 r"""
2025-07-02 05:46:45.876 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:45.883 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:45.889 synch point, and intraline difference marking is done on the
2025-07-02 05:46:45.895 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:45.903
2025-07-02 05:46:45.914 Example:
2025-07-02 05:46:45.922
2025-07-02 05:46:45.934 >>> d = Differ()
2025-07-02 05:46:45.940 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:45.947 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:45.955 >>> print(''.join(results), end="")
2025-07-02 05:46:45.966 - abcDefghiJkl
2025-07-02 05:46:45.983 + abcdefGhijkl
2025-07-02 05:46:46.003 """
2025-07-02 05:46:46.011
2025-07-02 05:46:46.018 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:46.030 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:46.043 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:46.052 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:46.061 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:46.067
2025-07-02 05:46:46.074 # search for the pair that matches best without being identical
2025-07-02 05:46:46.082 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:46.094 # on junk -- unless we have to)
2025-07-02 05:46:46.105 for j in range(blo, bhi):
2025-07-02 05:46:46.113 bj = b[j]
2025-07-02 05:46:46.126 cruncher.set_seq2(bj)
2025-07-02 05:46:46.137 for i in range(alo, ahi):
2025-07-02 05:46:46.146 ai = a[i]
2025-07-02 05:46:46.155 if ai == bj:
2025-07-02 05:46:46.162 if eqi is None:
2025-07-02 05:46:46.168 eqi, eqj = i, j
2025-07-02 05:46:46.174 continue
2025-07-02 05:46:46.180 cruncher.set_seq1(ai)
2025-07-02 05:46:46.187 # computing similarity is expensive, so use the quick
2025-07-02 05:46:46.193 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:46.199 # compares by a factor of 3.
2025-07-02 05:46:46.207 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:46.213 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:46.220 # of the computation is cached by cruncher
2025-07-02 05:46:46.226 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:46.232 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:46.238 cruncher.ratio() > best_ratio:
2025-07-02 05:46:46.248 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:46.260 if best_ratio < cutoff:
2025-07-02 05:46:46.269 # no non-identical "pretty close" pair
2025-07-02 05:46:46.277 if eqi is None:
2025-07-02 05:46:46.284 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:46.292 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:46.298 return
2025-07-02 05:46:46.310 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:46.317 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:46.324 else:
2025-07-02 05:46:46.332 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:46.338 eqi = None
2025-07-02 05:46:46.343
2025-07-02 05:46:46.350 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:46.356 # identical
2025-07-02 05:46:46.362
2025-07-02 05:46:46.377 # pump out diffs from before the synch point
2025-07-02 05:46:46.389 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:46.396
2025-07-02 05:46:46.403 # do intraline marking on the synch pair
2025-07-02 05:46:46.411 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:46.419 if eqi is None:
2025-07-02 05:46:46.425 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:46.431 atags = btags = ""
2025-07-02 05:46:46.438 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:46.443 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:46.448 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:46.454 if tag == 'replace':
2025-07-02 05:46:46.459 atags += '^' * la
2025-07-02 05:46:46.465 btags += '^' * lb
2025-07-02 05:46:46.471 elif tag == 'delete':
2025-07-02 05:46:46.477 atags += '-' * la
2025-07-02 05:46:46.482 elif tag == 'insert':
2025-07-02 05:46:46.491 btags += '+' * lb
2025-07-02 05:46:46.502 elif tag == 'equal':
2025-07-02 05:46:46.512 atags += ' ' * la
2025-07-02 05:46:46.519 btags += ' ' * lb
2025-07-02 05:46:46.525 else:
2025-07-02 05:46:46.531 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:46.538 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:46.545 else:
2025-07-02 05:46:46.552 # the synch pair is identical
2025-07-02 05:46:46.560 yield '  ' + aelt
2025-07-02 05:46:46.569
2025-07-02 05:46:46.582 # pump out diffs from after the synch point
2025-07-02 05:46:46.595 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:46.608
2025-07-02 05:46:46.622 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:46.633 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:46.643
2025-07-02 05:46:46.652 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:46.665 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:46.677 alo = 85, ahi = 1101
2025-07-02 05:46:46.691 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:46.704 blo = 85, bhi = 1101
2025-07-02 05:46:46.714
2025-07-02 05:46:46.726 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:46.739 g = []
2025-07-02 05:46:46.748 if alo < ahi:
2025-07-02 05:46:46.754 if blo < bhi:
2025-07-02 05:46:46.762 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:46.767 else:
2025-07-02 05:46:46.773 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:46.787 elif blo < bhi:
2025-07-02 05:46:46.796 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:46.804
2025-07-02 05:46:46.811 >       yield from g
2025-07-02 05:46:46.817
2025-07-02 05:46:46.822 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:46.827 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:46.832
2025-07-02 05:46:46.836 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:46.842 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:46.846 alo = 85, ahi = 1101
2025-07-02 05:46:46.856 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:46.864 blo = 85, bhi = 1101
2025-07-02 05:46:46.871
2025-07-02 05:46:46.879 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:46.886 r"""
2025-07-02 05:46:46.894 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:46.902 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:46.915 synch point, and intraline difference marking is done on the
2025-07-02 05:46:46.927 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:46.939
2025-07-02 05:46:46.953 Example:
2025-07-02 05:46:46.962
2025-07-02 05:46:46.970 >>> d = Differ()
2025-07-02 05:46:46.979 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:46.993 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:47.001 >>> print(''.join(results), end="")
2025-07-02 05:46:47.010 - abcDefghiJkl
2025-07-02 05:46:47.032 + abcdefGhijkl
2025-07-02 05:46:47.055 """
2025-07-02 05:46:47.068
2025-07-02 05:46:47.082 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:47.095 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:47.105 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:47.116 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:47.127 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:47.137
2025-07-02 05:46:47.145 # search for the pair that matches best without being identical
2025-07-02 05:46:47.151 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:47.156 # on junk -- unless we have to)
2025-07-02 05:46:47.162 for j in range(blo, bhi):
2025-07-02 05:46:47.172 bj = b[j]
2025-07-02 05:46:47.181 cruncher.set_seq2(bj)
2025-07-02 05:46:47.190 for i in range(alo, ahi):
2025-07-02 05:46:47.202 ai = a[i]
2025-07-02 05:46:47.212 if ai == bj:
2025-07-02 05:46:47.220 if eqi is None:
2025-07-02 05:46:47.233 eqi, eqj = i, j
2025-07-02 05:46:47.246 continue
2025-07-02 05:46:47.254 cruncher.set_seq1(ai)
2025-07-02 05:46:47.261 # computing similarity is expensive, so use the quick
2025-07-02 05:46:47.270 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:47.277 # compares by a factor of 3.
2025-07-02 05:46:47.284 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:47.292 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:47.299 # of the computation is cached by cruncher
2025-07-02 05:46:47.304 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:47.309 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:47.314 cruncher.ratio() > best_ratio:
2025-07-02 05:46:47.320 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:47.327 if best_ratio < cutoff:
2025-07-02 05:46:47.338 # no non-identical "pretty close" pair
2025-07-02 05:46:47.348 if eqi is None:
2025-07-02 05:46:47.357 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:47.364 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:47.371 return
2025-07-02 05:46:47.378 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:47.385 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:47.391 else:
2025-07-02 05:46:47.396 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:47.401 eqi = None
2025-07-02 05:46:47.407
2025-07-02 05:46:47.417 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:47.429 # identical
2025-07-02 05:46:47.439
2025-07-02 05:46:47.447 # pump out diffs from before the synch point
2025-07-02 05:46:47.454 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:47.461
2025-07-02 05:46:47.467 # do intraline marking on the synch pair
2025-07-02 05:46:47.474 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:47.479 if eqi is None:
2025-07-02 05:46:47.486 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:47.492 atags = btags = ""
2025-07-02 05:46:47.502 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:47.516 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:47.527 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:47.535 if tag == 'replace':
2025-07-02 05:46:47.543 atags += '^' * la
2025-07-02 05:46:47.551 btags += '^' * lb
2025-07-02 05:46:47.558 elif tag == 'delete':
2025-07-02 05:46:47.565 atags += '-' * la
2025-07-02 05:46:47.573 elif tag == 'insert':
2025-07-02 05:46:47.590 btags += '+' * lb
2025-07-02 05:46:47.601 elif tag == 'equal':
2025-07-02 05:46:47.609 atags += ' ' * la
2025-07-02 05:46:47.616 btags += ' ' * lb
2025-07-02 05:46:47.622 else:
2025-07-02 05:46:47.628 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:47.633 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:47.638 else:
2025-07-02 05:46:47.644 # the synch pair is identical
2025-07-02 05:46:47.651 yield '  ' + aelt
2025-07-02 05:46:47.657
2025-07-02 05:46:47.663 # pump out diffs from after the synch point
2025-07-02 05:46:47.669 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:47.679
2025-07-02 05:46:47.689 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:47.701 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:47.709
2025-07-02 05:46:47.716 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:47.723 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:47.729 alo = 86, ahi = 1101
2025-07-02 05:46:47.736 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:47.741 blo = 86, bhi = 1101
2025-07-02 05:46:47.746
2025-07-02 05:46:47.752 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:47.758 g = []
2025-07-02 05:46:47.764 if alo < ahi:
2025-07-02 05:46:47.770 if blo < bhi:
2025-07-02 05:46:47.780 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:47.788 else:
2025-07-02 05:46:47.798 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:47.806 elif blo < bhi:
2025-07-02 05:46:47.814 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:47.822
2025-07-02 05:46:47.833 >       yield from g
2025-07-02 05:46:47.843
2025-07-02 05:46:47.853 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:47.858 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:47.864
2025-07-02 05:46:47.870 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:47.876 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:47.882 alo = 86, ahi = 1101
2025-07-02 05:46:47.889 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:47.894 blo = 86, bhi = 1101
2025-07-02 05:46:47.904
2025-07-02 05:46:47.912 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:47.921 r"""
2025-07-02 05:46:47.931 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:47.944 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:47.955 synch point, and intraline difference marking is done on the
2025-07-02 05:46:47.964 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:47.972
2025-07-02 05:46:47.979 Example:
2025-07-02 05:46:47.990
2025-07-02 05:46:48.000 >>> d = Differ()
2025-07-02 05:46:48.008 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:48.014 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:48.019 >>> print(''.join(results), end="")
2025-07-02 05:46:48.026 - abcDefghiJkl
2025-07-02 05:46:48.039 + abcdefGhijkl
2025-07-02 05:46:48.052 """
2025-07-02 05:46:48.058
2025-07-02 05:46:48.065 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:48.076 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:48.089 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:48.100 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:48.109 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:48.116
2025-07-02 05:46:48.123 # search for the pair that matches best without being identical
2025-07-02 05:46:48.130 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:48.136 # on junk -- unless we have to)
2025-07-02 05:46:48.147 for j in range(blo, bhi):
2025-07-02 05:46:48.158 bj = b[j]
2025-07-02 05:46:48.167 cruncher.set_seq2(bj)
2025-07-02 05:46:48.178 for i in range(alo, ahi):
2025-07-02 05:46:48.187 ai = a[i]
2025-07-02 05:46:48.195 if ai == bj:
2025-07-02 05:46:48.203 if eqi is None:
2025-07-02 05:46:48.209 eqi, eqj = i, j
2025-07-02 05:46:48.214 continue
2025-07-02 05:46:48.226 cruncher.set_seq1(ai)
2025-07-02 05:46:48.235 # computing similarity is expensive, so use the quick
2025-07-02 05:46:48.244 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:48.257 # compares by a factor of 3.
2025-07-02 05:46:48.269 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:48.281 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:48.294 # of the computation is cached by cruncher
2025-07-02 05:46:48.309 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:48.317 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:48.324 cruncher.ratio() > best_ratio:
2025-07-02 05:46:48.332 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:48.338 if best_ratio < cutoff:
2025-07-02 05:46:48.348 # no non-identical "pretty close" pair
2025-07-02 05:46:48.357 if eqi is None:
2025-07-02 05:46:48.364 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:48.372 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:48.379 return
2025-07-02 05:46:48.385 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:48.392 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:48.399 else:
2025-07-02 05:46:48.407 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:48.418 eqi = None
2025-07-02 05:46:48.426
2025-07-02 05:46:48.438 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:48.451 # identical
2025-07-02 05:46:48.460
2025-07-02 05:46:48.473 # pump out diffs from before the synch point
2025-07-02 05:46:48.483 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:48.491
2025-07-02 05:46:48.501 # do intraline marking on the synch pair
2025-07-02 05:46:48.510 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:48.519 if eqi is None:
2025-07-02 05:46:48.527 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:48.533 atags = btags = ""
2025-07-02 05:46:48.539 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:48.545 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:48.551 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:48.559 if tag == 'replace':
2025-07-02 05:46:48.570 atags += '^' * la
2025-07-02 05:46:48.578 btags += '^' * lb
2025-07-02 05:46:48.586 elif tag == 'delete':
2025-07-02 05:46:48.596 atags += '-' * la
2025-07-02 05:46:48.606 elif tag == 'insert':
2025-07-02 05:46:48.618 btags += '+' * lb
2025-07-02 05:46:48.628 elif tag == 'equal':
2025-07-02 05:46:48.637 atags += ' ' * la
2025-07-02 05:46:48.647 btags += ' ' * lb
2025-07-02 05:46:48.655 else:
2025-07-02 05:46:48.663 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:48.671 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:48.683 else:
2025-07-02 05:46:48.693 # the synch pair is identical
2025-07-02 05:46:48.701 yield '  ' + aelt
2025-07-02 05:46:48.709
2025-07-02 05:46:48.720 # pump out diffs from after the synch point
2025-07-02 05:46:48.728 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:48.735
2025-07-02 05:46:48.743 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:48.749 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:48.755
2025-07-02 05:46:48.762 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:48.769 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:48.775 alo = 87, ahi = 1101
2025-07-02 05:46:48.782 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:48.792 blo = 87, bhi = 1101
2025-07-02 05:46:48.798
2025-07-02 05:46:48.804 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:48.810 g = []
2025-07-02 05:46:48.816 if alo < ahi:
2025-07-02 05:46:48.822 if blo < bhi:
2025-07-02 05:46:48.831 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:48.839 else:
2025-07-02 05:46:48.853 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:48.863 elif blo < bhi:
2025-07-02 05:46:48.874 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:48.883
2025-07-02 05:46:48.895 >       yield from g
2025-07-02 05:46:48.903
2025-07-02 05:46:48.910 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:48.916 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:48.922
2025-07-02 05:46:48.927 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:48.934 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:48.940 alo = 87, ahi = 1101
2025-07-02 05:46:48.953 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:48.964 blo = 87, bhi = 1101
2025-07-02 05:46:48.974
2025-07-02 05:46:48.983 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:48.990 r"""
2025-07-02 05:46:48.996 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:49.002 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:49.010 synch point, and intraline difference marking is done on the
2025-07-02 05:46:49.016 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:49.024
2025-07-02 05:46:49.031 Example:
2025-07-02 05:46:49.038
2025-07-02 05:46:49.047 >>> d = Differ()
2025-07-02 05:46:49.059 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:49.070 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:49.080 >>> print(''.join(results), end="")
2025-07-02 05:46:49.087 - abcDefghiJkl
2025-07-02 05:46:49.102 + abcdefGhijkl
2025-07-02 05:46:49.115 """
2025-07-02 05:46:49.125
2025-07-02 05:46:49.138 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:49.147 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:49.155 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:49.163 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:49.170 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:49.180
2025-07-02 05:46:49.192 # search for the pair that matches best without being identical
2025-07-02 05:46:49.202 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:49.211 # on junk -- unless we have to)
2025-07-02 05:46:49.217 for j in range(blo, bhi):
2025-07-02 05:46:49.224 bj = b[j]
2025-07-02 05:46:49.230 cruncher.set_seq2(bj)
2025-07-02 05:46:49.240 for i in range(alo, ahi):
2025-07-02 05:46:49.250 ai = a[i]
2025-07-02 05:46:49.257 if ai == bj:
2025-07-02 05:46:49.263 if eqi is None:
2025-07-02 05:46:49.271 eqi, eqj = i, j
2025-07-02 05:46:49.283 continue
2025-07-02 05:46:49.293 cruncher.set_seq1(ai)
2025-07-02 05:46:49.301 # computing similarity is expensive, so use the quick
2025-07-02 05:46:49.308 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:49.315 # compares by a factor of 3.
2025-07-02 05:46:49.321 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:49.330 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:49.341 # of the computation is cached by cruncher
2025-07-02 05:46:49.349 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:49.361 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:49.373 cruncher.ratio() > best_ratio:
2025-07-02 05:46:49.384 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:49.395 if best_ratio < cutoff:
2025-07-02 05:46:49.404 # no non-identical "pretty close" pair
2025-07-02 05:46:49.413 if eqi is None:
2025-07-02 05:46:49.419 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:49.426 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:49.431 return
2025-07-02 05:46:49.436 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:49.442 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:49.447 else:
2025-07-02 05:46:49.453 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:49.459 eqi = None
2025-07-02 05:46:49.465
2025-07-02 05:46:49.470 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:49.475 # identical
2025-07-02 05:46:49.480
2025-07-02 05:46:49.487 # pump out diffs from before the synch point
2025-07-02 05:46:49.493 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:49.499
2025-07-02 05:46:49.504 # do intraline marking on the synch pair
2025-07-02 05:46:49.510 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:49.522 if eqi is None:
2025-07-02 05:46:49.532 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:49.540 atags = btags = ""
2025-07-02 05:46:49.547 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:49.555 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:49.566 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:49.574 if tag == 'replace':
2025-07-02 05:46:49.583 atags += '^' * la
2025-07-02 05:46:49.590 btags += '^' * lb
2025-07-02 05:46:49.596 elif tag == 'delete':
2025-07-02 05:46:49.601 atags += '-' * la
2025-07-02 05:46:49.607 elif tag == 'insert':
2025-07-02 05:46:49.614 btags += '+' * lb
2025-07-02 05:46:49.625 elif tag == 'equal':
2025-07-02 05:46:49.634 atags += ' ' * la
2025-07-02 05:46:49.643 btags += ' ' * lb
2025-07-02 05:46:49.655 else:
2025-07-02 05:46:49.665 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:49.671 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:49.677 else:
2025-07-02 05:46:49.682 # the synch pair is identical
2025-07-02 05:46:49.686 yield '  ' + aelt
2025-07-02 05:46:49.691
2025-07-02 05:46:49.700 # pump out diffs from after the synch point
2025-07-02 05:46:49.711 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:49.719
2025-07-02 05:46:49.727 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:49.734 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:49.745
2025-07-02 05:46:49.754 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:49.761 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:49.767 alo = 88, ahi = 1101
2025-07-02 05:46:49.775 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:49.785 blo = 88, bhi = 1101
2025-07-02 05:46:49.795
2025-07-02 05:46:49.807 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:49.817 g = []
2025-07-02 05:46:49.826 if alo < ahi:
2025-07-02 05:46:49.834 if blo < bhi:
2025-07-02 05:46:49.841 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:49.847 else:
2025-07-02 05:46:49.854 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:49.860 elif blo < bhi:
2025-07-02 05:46:49.867 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:49.876
2025-07-02 05:46:49.887 >       yield from g
2025-07-02 05:46:49.896
2025-07-02 05:46:49.902 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:49.911 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:49.919
2025-07-02 05:46:49.926 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:49.934 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:49.943 alo = 88, ahi = 1101
2025-07-02 05:46:49.952 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:49.959 blo = 88, bhi = 1101
2025-07-02 05:46:49.971
2025-07-02 05:46:49.980 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:49.986 r"""
2025-07-02 05:46:49.992 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:49.999 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:50.008 synch point, and intraline difference marking is done on the
2025-07-02 05:46:50.021 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:50.031
2025-07-02 05:46:50.039 Example:
2025-07-02 05:46:50.050
2025-07-02 05:46:50.058 >>> d = Differ()
2025-07-02 05:46:50.069 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:50.080 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:50.089 >>> print(''.join(results), end="")
2025-07-02 05:46:50.099 - abcDefghiJkl
2025-07-02 05:46:50.121 + abcdefGhijkl
2025-07-02 05:46:50.133 """
2025-07-02 05:46:50.138
2025-07-02 05:46:50.142 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:50.148 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:50.155 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:50.161 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:50.169 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:50.177
2025-07-02 05:46:50.183 # search for the pair that matches best without being identical
2025-07-02 05:46:50.188 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:50.193 # on junk -- unless we have to)
2025-07-02 05:46:50.200 for j in range(blo, bhi):
2025-07-02 05:46:50.206 bj = b[j]
2025-07-02 05:46:50.212 cruncher.set_seq2(bj)
2025-07-02 05:46:50.218 for i in range(alo, ahi):
2025-07-02 05:46:50.224 ai = a[i]
2025-07-02 05:46:50.230 if ai == bj:
2025-07-02 05:46:50.237 if eqi is None:
2025-07-02 05:46:50.243 eqi, eqj = i, j
2025-07-02 05:46:50.252 continue
2025-07-02 05:46:50.263 cruncher.set_seq1(ai)
2025-07-02 05:46:50.272 # computing similarity is expensive, so use the quick
2025-07-02 05:46:50.279 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:50.286 # compares by a factor of 3.
2025-07-02 05:46:50.297 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:50.307 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:50.317 # of the computation is cached by cruncher
2025-07-02 05:46:50.329 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:50.337 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:50.345 cruncher.ratio() > best_ratio:
2025-07-02 05:46:50.351 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:50.365 if best_ratio < cutoff:
2025-07-02 05:46:50.377 # no non-identical "pretty close" pair
2025-07-02 05:46:50.389 if eqi is None:
2025-07-02 05:46:50.396 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:50.402 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:50.409 return
2025-07-02 05:46:50.417 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:50.425 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:50.431 else:
2025-07-02 05:46:50.439 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:50.445 eqi = None
2025-07-02 05:46:50.451
2025-07-02 05:46:50.459 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:50.470 # identical
2025-07-02 05:46:50.479
2025-07-02 05:46:50.492 # pump out diffs from before the synch point
2025-07-02 05:46:50.504 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:50.515
2025-07-02 05:46:50.523 # do intraline marking on the synch pair
2025-07-02 05:46:50.531 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:50.537 if eqi is None:
2025-07-02 05:46:50.543 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:50.548 atags = btags = ""
2025-07-02 05:46:50.553 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:50.558 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:50.563 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:50.570 if tag == 'replace':
2025-07-02 05:46:50.581 atags += '^' * la
2025-07-02 05:46:50.589 btags += '^' * lb
2025-07-02 05:46:50.596 elif tag == 'delete':
2025-07-02 05:46:50.602 atags += '-' * la
2025-07-02 05:46:50.614 elif tag == 'insert':
2025-07-02 05:46:50.625 btags += '+' * lb
2025-07-02 05:46:50.635 elif tag == 'equal':
2025-07-02 05:46:50.645 atags += ' ' * la
2025-07-02 05:46:50.654 btags += ' ' * lb
2025-07-02 05:46:50.660 else:
2025-07-02 05:46:50.667 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:50.673 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:50.679 else:
2025-07-02 05:46:50.686 # the synch pair is identical
2025-07-02 05:46:50.692 yield '  ' + aelt
2025-07-02 05:46:50.703
2025-07-02 05:46:50.713 # pump out diffs from after the synch point
2025-07-02 05:46:50.725 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:50.736
2025-07-02 05:46:50.749 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:50.762 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:50.773
2025-07-02 05:46:50.780 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:50.793 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:50.803 alo = 89, ahi = 1101
2025-07-02 05:46:50.811 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:50.818 blo = 89, bhi = 1101
2025-07-02 05:46:50.824
2025-07-02 05:46:50.831 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:50.836 g = []
2025-07-02 05:46:50.842 if alo < ahi:
2025-07-02 05:46:50.848 if blo < bhi:
2025-07-02 05:46:50.854 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:50.860 else:
2025-07-02 05:46:50.866 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:50.877 elif blo < bhi:
2025-07-02 05:46:50.890 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:50.901
2025-07-02 05:46:50.912 >       yield from g
2025-07-02 05:46:50.924
2025-07-02 05:46:50.935 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:50.947 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:50.959
2025-07-02 05:46:50.970 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:50.981 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:50.988 alo = 89, ahi = 1101
2025-07-02 05:46:51.001 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:51.011 blo = 89, bhi = 1101
2025-07-02 05:46:51.022
2025-07-02 05:46:51.032 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:51.039 r"""
2025-07-02 05:46:51.046 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:51.055 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:51.066 synch point, and intraline difference marking is done on the
2025-07-02 05:46:51.074 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:51.082
2025-07-02 05:46:51.093 Example:
2025-07-02 05:46:51.101
2025-07-02 05:46:51.109 >>> d = Differ()
2025-07-02 05:46:51.116 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:51.122 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:51.134 >>> print(''.join(results), end="")
2025-07-02 05:46:51.146 - abcDefghiJkl
2025-07-02 05:46:51.172 + abcdefGhijkl
2025-07-02 05:46:51.193 """
2025-07-02 05:46:51.203
2025-07-02 05:46:51.212 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:51.219 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:51.225 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:51.231 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:51.237 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:51.242
2025-07-02 05:46:51.249 # search for the pair that matches best without being identical
2025-07-02 05:46:51.255 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:51.260 # on junk -- unless we have to)
2025-07-02 05:46:51.266 for j in range(blo, bhi):
2025-07-02 05:46:51.271 bj = b[j]
2025-07-02 05:46:51.278 cruncher.set_seq2(bj)
2025-07-02 05:46:51.289 for i in range(alo, ahi):
2025-07-02 05:46:51.297 ai = a[i]
2025-07-02 05:46:51.303 if ai == bj:
2025-07-02 05:46:51.309 if eqi is None:
2025-07-02 05:46:51.315 eqi, eqj = i, j
2025-07-02 05:46:51.323 continue
2025-07-02 05:46:51.335 cruncher.set_seq1(ai)
2025-07-02 05:46:51.344 # computing similarity is expensive, so use the quick
2025-07-02 05:46:51.352 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:51.359 # compares by a factor of 3.
2025-07-02 05:46:51.365 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:51.370 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:51.376 # of the computation is cached by cruncher
2025-07-02 05:46:51.386 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:51.395 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:51.404 cruncher.ratio() > best_ratio:
2025-07-02 05:46:51.411 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:51.417 if best_ratio < cutoff:
2025-07-02 05:46:51.428 # no non-identical "pretty close" pair
2025-07-02 05:46:51.439 if eqi is None:
2025-07-02 05:46:51.450 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:51.461 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:51.468 return
2025-07-02 05:46:51.474 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:51.479 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:51.484 else:
2025-07-02 05:46:51.488 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:51.493 eqi = None
2025-07-02 05:46:51.498
2025-07-02 05:46:51.503 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:51.507 # identical
2025-07-02 05:46:51.512
2025-07-02 05:46:51.518 # pump out diffs from before the synch point
2025-07-02 05:46:51.529 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:51.538
2025-07-02 05:46:51.544 # do intraline marking on the synch pair
2025-07-02 05:46:51.550 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:51.555 if eqi is None:
2025-07-02 05:46:51.560 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:51.565 atags = btags = ""
2025-07-02 05:46:51.570 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:51.575 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:51.583 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:51.593 if tag == 'replace':
2025-07-02 05:46:51.601 atags += '^' * la
2025-07-02 05:46:51.608 btags += '^' * lb
2025-07-02 05:46:51.614 elif tag == 'delete':
2025-07-02 05:46:51.626 atags += '-' * la
2025-07-02 05:46:51.637 elif tag == 'insert':
2025-07-02 05:46:51.649 btags += '+' * lb
2025-07-02 05:46:51.660 elif tag == 'equal':
2025-07-02 05:46:51.669 atags += ' ' * la
2025-07-02 05:46:51.676 btags += ' ' * lb
2025-07-02 05:46:51.683 else:
2025-07-02 05:46:51.691 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:51.704 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:51.713 else:
2025-07-02 05:46:51.721 # the synch pair is identical
2025-07-02 05:46:51.728 yield '  ' + aelt
2025-07-02 05:46:51.735
2025-07-02 05:46:51.742 # pump out diffs from after the synch point
2025-07-02 05:46:51.748 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:51.755
2025-07-02 05:46:51.765 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:51.773 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:51.783
2025-07-02 05:46:51.799 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:51.809 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:51.816 alo = 92, ahi = 1101
2025-07-02 05:46:51.823 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:51.829 blo = 92, bhi = 1101
2025-07-02 05:46:51.836
2025-07-02 05:46:51.842 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:51.848 g = []
2025-07-02 05:46:51.854 if alo < ahi:
2025-07-02 05:46:51.864 if blo < bhi:
2025-07-02 05:46:51.876 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:51.887 else:
2025-07-02 05:46:51.896 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:51.907 elif blo < bhi:
2025-07-02 05:46:51.919 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:51.929
2025-07-02 05:46:51.937 >       yield from g
2025-07-02 05:46:51.945
2025-07-02 05:46:51.951 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:51.958 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:51.964
2025-07-02 05:46:51.971 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:51.979 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:51.985 alo = 92, ahi = 1101
2025-07-02 05:46:51.997 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:52.007 blo = 92, bhi = 1101
2025-07-02 05:46:52.015
2025-07-02 05:46:52.023 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:52.030 r"""
2025-07-02 05:46:52.041 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:52.051 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:52.058 synch point, and intraline difference marking is done on the
2025-07-02 05:46:52.065 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:52.071
2025-07-02 05:46:52.076 Example:
2025-07-02 05:46:52.083
2025-07-02 05:46:52.092 >>> d = Differ()
2025-07-02 05:46:52.100 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:52.106 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:52.112 >>> print(''.join(results), end="")
2025-07-02 05:46:52.118 - abcDefghiJkl
2025-07-02 05:46:52.135 + abcdefGhijkl
2025-07-02 05:46:52.156 """
2025-07-02 05:46:52.166
2025-07-02 05:46:52.175 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:52.187 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:52.196 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:52.204 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:52.211 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:52.219
2025-07-02 05:46:52.229 # search for the pair that matches best without being identical
2025-07-02 05:46:52.238 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:52.246 # on junk -- unless we have to)
2025-07-02 05:46:52.256 for j in range(blo, bhi):
2025-07-02 05:46:52.264 bj = b[j]
2025-07-02 05:46:52.272 cruncher.set_seq2(bj)
2025-07-02 05:46:52.279 for i in range(alo, ahi):
2025-07-02 05:46:52.287 ai = a[i]
2025-07-02 05:46:52.299 if ai == bj:
2025-07-02 05:46:52.308 if eqi is None:
2025-07-02 05:46:52.316 eqi, eqj = i, j
2025-07-02 05:46:52.322 continue
2025-07-02 05:46:52.331 cruncher.set_seq1(ai)
2025-07-02 05:46:52.338 # computing similarity is expensive, so use the quick
2025-07-02 05:46:52.346 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:52.352 # compares by a factor of 3.
2025-07-02 05:46:52.361 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:52.373 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:52.380 # of the computation is cached by cruncher
2025-07-02 05:46:52.389 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:52.401 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:52.411 cruncher.ratio() > best_ratio:
2025-07-02 05:46:52.419 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:52.425 if best_ratio < cutoff:
2025-07-02 05:46:52.435 # no non-identical "pretty close" pair
2025-07-02 05:46:52.442 if eqi is None:
2025-07-02 05:46:52.451 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:52.459 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:52.466 return
2025-07-02 05:46:52.472 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:52.478 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:52.491 else:
2025-07-02 05:46:52.504 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:52.515 eqi = None
2025-07-02 05:46:52.524
2025-07-02 05:46:52.532 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:52.539 # identical
2025-07-02 05:46:52.547
2025-07-02 05:46:52.555 # pump out diffs from before the synch point
2025-07-02 05:46:52.563 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:52.570
2025-07-02 05:46:52.579 # do intraline marking on the synch pair
2025-07-02 05:46:52.591 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:52.600 if eqi is None:
2025-07-02 05:46:52.607 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:52.614 atags = btags = ""
2025-07-02 05:46:52.620 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:52.627 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:52.635 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:52.642 if tag == 'replace':
2025-07-02 05:46:52.651 atags += '^' * la
2025-07-02 05:46:52.662 btags += '^' * lb
2025-07-02 05:46:52.671 elif tag == 'delete':
2025-07-02 05:46:52.681 atags += '-' * la
2025-07-02 05:46:52.690 elif tag == 'insert':
2025-07-02 05:46:52.697 btags += '+' * lb
2025-07-02 05:46:52.703 elif tag == 'equal':
2025-07-02 05:46:52.711 atags += ' ' * la
2025-07-02 05:46:52.718 btags += ' ' * lb
2025-07-02 05:46:52.725 else:
2025-07-02 05:46:52.734 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:52.746 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:52.755 else:
2025-07-02 05:46:52.761 # the synch pair is identical
2025-07-02 05:46:52.767 yield '  ' + aelt
2025-07-02 05:46:52.773
2025-07-02 05:46:52.779 # pump out diffs from after the synch point
2025-07-02 05:46:52.787 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:52.794
2025-07-02 05:46:52.803 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:52.810 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:52.821
2025-07-02 05:46:52.831 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:52.839 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:52.847 alo = 93, ahi = 1101
2025-07-02 05:46:52.858 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:52.870 blo = 93, bhi = 1101
2025-07-02 05:46:52.881
2025-07-02 05:46:52.892 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:52.900 g = []
2025-07-02 05:46:52.908 if alo < ahi:
2025-07-02 05:46:52.916 if blo < bhi:
2025-07-02 05:46:52.923 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:52.929 else:
2025-07-02 05:46:52.935 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:52.945 elif blo < bhi:
2025-07-02 05:46:52.958 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:52.970
2025-07-02 05:46:52.983 >       yield from g
2025-07-02 05:46:52.997
2025-07-02 05:46:53.007 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:53.017 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:53.025
2025-07-02 05:46:53.032 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:53.045 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:53.054 alo = 93, ahi = 1101
2025-07-02 05:46:53.062 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:53.069 blo = 93, bhi = 1101
2025-07-02 05:46:53.075
2025-07-02 05:46:53.082 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:53.089 r"""
2025-07-02 05:46:53.097 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:53.104 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:53.112 synch point, and intraline difference marking is done on the
2025-07-02 05:46:53.124 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:53.134
2025-07-02 05:46:53.141 Example:
2025-07-02 05:46:53.148
2025-07-02 05:46:53.153 >>> d = Differ()
2025-07-02 05:46:53.159 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:53.164 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:53.169 >>> print(''.join(results), end="")
2025-07-02 05:46:53.174 - abcDefghiJkl
2025-07-02 05:46:53.184 + abcdefGhijkl
2025-07-02 05:46:53.198 """
2025-07-02 05:46:53.205
2025-07-02 05:46:53.212 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:53.219 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:53.226 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:53.236 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:53.245 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:53.252
2025-07-02 05:46:53.259 # search for the pair that matches best without being identical
2025-07-02 05:46:53.265 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:53.270 # on junk -- unless we have to)
2025-07-02 05:46:53.276 for j in range(blo, bhi):
2025-07-02 05:46:53.281 bj = b[j]
2025-07-02 05:46:53.287 cruncher.set_seq2(bj)
2025-07-02 05:46:53.292 for i in range(alo, ahi):
2025-07-02 05:46:53.299 ai = a[i]
2025-07-02 05:46:53.305 if ai == bj:
2025-07-02 05:46:53.312 if eqi is None:
2025-07-02 05:46:53.318 eqi, eqj = i, j
2025-07-02 05:46:53.327 continue
2025-07-02 05:46:53.333 cruncher.set_seq1(ai)
2025-07-02 05:46:53.339 # computing similarity is expensive, so use the quick
2025-07-02 05:46:53.346 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:53.352 # compares by a factor of 3.
2025-07-02 05:46:53.358 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:53.369 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:53.378 # of the computation is cached by cruncher
2025-07-02 05:46:53.386 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:53.394 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:53.405 cruncher.ratio() > best_ratio:
2025-07-02 05:46:53.414 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:53.421 if best_ratio < cutoff:
2025-07-02 05:46:53.427 # no non-identical "pretty close" pair
2025-07-02 05:46:53.434 if eqi is None:
2025-07-02 05:46:53.439 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:53.444 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:53.449 return
2025-07-02 05:46:53.454 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:53.459 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:53.464 else:
2025-07-02 05:46:53.469 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:53.474 eqi = None
2025-07-02 05:46:53.479
2025-07-02 05:46:53.493 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:53.503 # identical
2025-07-02 05:46:53.511
2025-07-02 05:46:53.526 # pump out diffs from before the synch point
2025-07-02 05:46:53.539 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:53.548
2025-07-02 05:46:53.555 # do intraline marking on the synch pair
2025-07-02 05:46:53.561 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:53.567 if eqi is None:
2025-07-02 05:46:53.574 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:53.582 atags = btags = ""
2025-07-02 05:46:53.596 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:53.606 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:53.617 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:53.627 if tag == 'replace':
2025-07-02 05:46:53.636 atags += '^' * la
2025-07-02 05:46:53.644 btags += '^' * lb
2025-07-02 05:46:53.650 elif tag == 'delete':
2025-07-02 05:46:53.657 atags += '-' * la
2025-07-02 05:46:53.663 elif tag == 'insert':
2025-07-02 05:46:53.671 btags += '+' * lb
2025-07-02 05:46:53.681 elif tag == 'equal':
2025-07-02 05:46:53.693 atags += ' ' * la
2025-07-02 05:46:53.706 btags += ' ' * lb
2025-07-02 05:46:53.717 else:
2025-07-02 05:46:53.728 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:53.736 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:53.744 else:
2025-07-02 05:46:53.752 # the synch pair is identical
2025-07-02 05:46:53.758 yield '  ' + aelt
2025-07-02 05:46:53.768
2025-07-02 05:46:53.780 # pump out diffs from after the synch point
2025-07-02 05:46:53.790 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:53.799
2025-07-02 05:46:53.808 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:53.815 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:53.822
2025-07-02 05:46:53.828 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:53.840 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:53.852 alo = 94, ahi = 1101
2025-07-02 05:46:53.861 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:53.868 blo = 94, bhi = 1101
2025-07-02 05:46:53.874
2025-07-02 05:46:53.881 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:53.886 g = []
2025-07-02 05:46:53.896 if alo < ahi:
2025-07-02 05:46:53.905 if blo < bhi:
2025-07-02 05:46:53.912 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:53.919 else:
2025-07-02 05:46:53.925 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:53.931 elif blo < bhi:
2025-07-02 05:46:53.936 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:53.942
2025-07-02 05:46:53.946 >       yield from g
2025-07-02 05:46:53.951
2025-07-02 05:46:53.957 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:53.963 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:53.968
2025-07-02 05:46:53.975 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:53.983 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:53.995 alo = 94, ahi = 1101
2025-07-02 05:46:54.010 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:54.023 blo = 94, bhi = 1101
2025-07-02 05:46:54.032
2025-07-02 05:46:54.038 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:54.045 r"""
2025-07-02 05:46:54.052 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:54.058 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:54.064 synch point, and intraline difference marking is done on the
2025-07-02 05:46:54.071 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:54.078
2025-07-02 05:46:54.087 Example:
2025-07-02 05:46:54.098
2025-07-02 05:46:54.107 >>> d = Differ()
2025-07-02 05:46:54.114 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:54.120 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:54.126 >>> print(''.join(results), end="")
2025-07-02 05:46:54.131 - abcDefghiJkl
2025-07-02 05:46:54.140 + abcdefGhijkl
2025-07-02 05:46:54.151 """
2025-07-02 05:46:54.157
2025-07-02 05:46:54.163 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:54.170 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:54.179 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:54.188 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:54.195 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:54.201
2025-07-02 05:46:54.206 # search for the pair that matches best without being identical
2025-07-02 05:46:54.210 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:54.215 # on junk -- unless we have to)
2025-07-02 05:46:54.220 for j in range(blo, bhi):
2025-07-02 05:46:54.224 bj = b[j]
2025-07-02 05:46:54.229 cruncher.set_seq2(bj)
2025-07-02 05:46:54.234 for i in range(alo, ahi):
2025-07-02 05:46:54.246 ai = a[i]
2025-07-02 05:46:54.256 if ai == bj:
2025-07-02 05:46:54.267 if eqi is None:
2025-07-02 05:46:54.277 eqi, eqj = i, j
2025-07-02 05:46:54.284 continue
2025-07-02 05:46:54.290 cruncher.set_seq1(ai)
2025-07-02 05:46:54.296 # computing similarity is expensive, so use the quick
2025-07-02 05:46:54.301 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:54.306 # compares by a factor of 3.
2025-07-02 05:46:54.311 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:54.317 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:54.324 # of the computation is cached by cruncher
2025-07-02 05:46:54.330 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:54.337 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:54.342 cruncher.ratio() > best_ratio:
2025-07-02 05:46:54.349 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:54.355 if best_ratio < cutoff:
2025-07-02 05:46:54.361 # no non-identical "pretty close" pair
2025-07-02 05:46:54.367 if eqi is None:
2025-07-02 05:46:54.377 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:54.390 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:54.395 return
2025-07-02 05:46:54.400 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:54.406 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:54.411 else:
2025-07-02 05:46:54.421 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:54.431 eqi = None
2025-07-02 05:46:54.438
2025-07-02 05:46:54.445 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:54.457 # identical
2025-07-02 05:46:54.467
2025-07-02 05:46:54.476 # pump out diffs from before the synch point
2025-07-02 05:46:54.483 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:54.491
2025-07-02 05:46:54.501 # do intraline marking on the synch pair
2025-07-02 05:46:54.510 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:54.519 if eqi is None:
2025-07-02 05:46:54.526 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:54.536 atags = btags = ""
2025-07-02 05:46:54.545 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:54.553 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:54.560 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:54.567 if tag == 'replace':
2025-07-02 05:46:54.575 atags += '^' * la
2025-07-02 05:46:54.587 btags += '^' * lb
2025-07-02 05:46:54.601 elif tag == 'delete':
2025-07-02 05:46:54.611 atags += '-' * la
2025-07-02 05:46:54.623 elif tag == 'insert':
2025-07-02 05:46:54.632 btags += '+' * lb
2025-07-02 05:46:54.639 elif tag == 'equal':
2025-07-02 05:46:54.645 atags += ' ' * la
2025-07-02 05:46:54.651 btags += ' ' * lb
2025-07-02 05:46:54.656 else:
2025-07-02 05:46:54.662 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:54.669 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:54.674 else:
2025-07-02 05:46:54.679 # the synch pair is identical
2025-07-02 05:46:54.683 yield '  ' + aelt
2025-07-02 05:46:54.688
2025-07-02 05:46:54.693 # pump out diffs from after the synch point
2025-07-02 05:46:54.700 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:54.707
2025-07-02 05:46:54.713 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:54.718 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:54.725
2025-07-02 05:46:54.732 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:54.738 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:54.744 alo = 95, ahi = 1101
2025-07-02 05:46:54.751 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:54.758 blo = 95, bhi = 1101
2025-07-02 05:46:54.764
2025-07-02 05:46:54.771 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:54.777 g = []
2025-07-02 05:46:54.783 if alo < ahi:
2025-07-02 05:46:54.790 if blo < bhi:
2025-07-02 05:46:54.799 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:54.810 else:
2025-07-02 05:46:54.821 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:54.832 elif blo < bhi:
2025-07-02 05:46:54.840 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:54.847
2025-07-02 05:46:54.861 >       yield from g
2025-07-02 05:46:54.872
2025-07-02 05:46:54.885 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:54.897 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:54.907
2025-07-02 05:46:54.920 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:54.930 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:54.938 alo = 95, ahi = 1101
2025-07-02 05:46:54.947 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:54.955 blo = 95, bhi = 1101
2025-07-02 05:46:54.961
2025-07-02 05:46:54.968 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:54.974 r"""
2025-07-02 05:46:54.985 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:54.997 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:55.009 synch point, and intraline difference marking is done on the
2025-07-02 05:46:55.019 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:55.031
2025-07-02 05:46:55.040 Example:
2025-07-02 05:46:55.047
2025-07-02 05:46:55.055 >>> d = Differ()
2025-07-02 05:46:55.061 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:55.073 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:55.083 >>> print(''.join(results), end="")
2025-07-02 05:46:55.092 - abcDefghiJkl
2025-07-02 05:46:55.111 + abcdefGhijkl
2025-07-02 05:46:55.131 """
2025-07-02 05:46:55.139
2025-07-02 05:46:55.146 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:55.156 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:55.167 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:55.177 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:55.187 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:55.198
2025-07-02 05:46:55.209 # search for the pair that matches best without being identical
2025-07-02 05:46:55.220 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:55.229 # on junk -- unless we have to)
2025-07-02 05:46:55.237 for j in range(blo, bhi):
2025-07-02 05:46:55.245 bj = b[j]
2025-07-02 05:46:55.252 cruncher.set_seq2(bj)
2025-07-02 05:46:55.258 for i in range(alo, ahi):
2025-07-02 05:46:55.264 ai = a[i]
2025-07-02 05:46:55.270 if ai == bj:
2025-07-02 05:46:55.281 if eqi is None:
2025-07-02 05:46:55.292 eqi, eqj = i, j
2025-07-02 05:46:55.299 continue
2025-07-02 05:46:55.308 cruncher.set_seq1(ai)
2025-07-02 05:46:55.316 # computing similarity is expensive, so use the quick
2025-07-02 05:46:55.322 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:55.329 # compares by a factor of 3.
2025-07-02 05:46:55.335 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:55.342 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:55.352 # of the computation is cached by cruncher
2025-07-02 05:46:55.362 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:55.370 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:55.377 cruncher.ratio() > best_ratio:
2025-07-02 05:46:55.384 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:55.391 if best_ratio < cutoff:
2025-07-02 05:46:55.398 # no non-identical "pretty close" pair
2025-07-02 05:46:55.408 if eqi is None:
2025-07-02 05:46:55.418 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:55.427 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:55.436 return
2025-07-02 05:46:55.448 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:55.457 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:55.464 else:
2025-07-02 05:46:55.472 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:55.479 eqi = None
2025-07-02 05:46:55.489
2025-07-02 05:46:55.499 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:55.508 # identical
2025-07-02 05:46:55.514
2025-07-02 05:46:55.519 # pump out diffs from before the synch point
2025-07-02 05:46:55.524 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:55.532
2025-07-02 05:46:55.544 # do intraline marking on the synch pair
2025-07-02 05:46:55.556 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:55.566 if eqi is None:
2025-07-02 05:46:55.574 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:55.586 atags = btags = ""
2025-07-02 05:46:55.597 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:55.609 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:55.620 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:55.629 if tag == 'replace':
2025-07-02 05:46:55.639 atags += '^' * la
2025-07-02 05:46:55.647 btags += '^' * lb
2025-07-02 05:46:55.654 elif tag == 'delete':
2025-07-02 05:46:55.663 atags += '-' * la
2025-07-02 05:46:55.673 elif tag == 'insert':
2025-07-02 05:46:55.682 btags += '+' * lb
2025-07-02 05:46:55.691 elif tag == 'equal':
2025-07-02 05:46:55.700 atags += ' ' * la
2025-07-02 05:46:55.707 btags += ' ' * lb
2025-07-02 05:46:55.713 else:
2025-07-02 05:46:55.718 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:55.723 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:55.728 else:
2025-07-02 05:46:55.733 # the synch pair is identical
2025-07-02 05:46:55.738 yield '  ' + aelt
2025-07-02 05:46:55.743
2025-07-02 05:46:55.748 # pump out diffs from after the synch point
2025-07-02 05:46:55.754 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:55.764
2025-07-02 05:46:55.774 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:55.783 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:55.790
2025-07-02 05:46:55.801 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:55.810 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:55.815 alo = 96, ahi = 1101
2025-07-02 05:46:55.821 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:55.826 blo = 96, bhi = 1101
2025-07-02 05:46:55.830
2025-07-02 05:46:55.835 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:55.839 g = []
2025-07-02 05:46:55.844 if alo < ahi:
2025-07-02 05:46:55.848 if blo < bhi:
2025-07-02 05:46:55.853 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:55.857 else:
2025-07-02 05:46:55.861 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:55.866 elif blo < bhi:
2025-07-02 05:46:55.871 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:55.885
2025-07-02 05:46:55.892 >       yield from g
2025-07-02 05:46:55.898
2025-07-02 05:46:55.911 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:55.921 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:55.928
2025-07-02 05:46:55.936 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:55.944 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:55.950 alo = 96, ahi = 1101
2025-07-02 05:46:55.958 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:55.970 blo = 96, bhi = 1101
2025-07-02 05:46:55.981
2025-07-02 05:46:55.994 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:56.006 r"""
2025-07-02 05:46:56.018 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:56.030 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:56.040 synch point, and intraline difference marking is done on the
2025-07-02 05:46:56.050 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:56.059
2025-07-02 05:46:56.071 Example:
2025-07-02 05:46:56.082
2025-07-02 05:46:56.092 >>> d = Differ()
2025-07-02 05:46:56.100 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:56.107 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:56.114 >>> print(''.join(results), end="")
2025-07-02 05:46:56.126 - abcDefghiJkl
2025-07-02 05:46:56.141 + abcdefGhijkl
2025-07-02 05:46:56.154 """
2025-07-02 05:46:56.165
2025-07-02 05:46:56.175 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:56.184 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:56.192 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:56.200 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:56.206 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:56.217
2025-07-02 05:46:56.228 # search for the pair that matches best without being identical
2025-07-02 05:46:56.237 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:56.243 # on junk -- unless we have to)
2025-07-02 05:46:56.250 for j in range(blo, bhi):
2025-07-02 05:46:56.256 bj = b[j]
2025-07-02 05:46:56.262 cruncher.set_seq2(bj)
2025-07-02 05:46:56.271 for i in range(alo, ahi):
2025-07-02 05:46:56.279 ai = a[i]
2025-07-02 05:46:56.286 if ai == bj:
2025-07-02 05:46:56.293 if eqi is None:
2025-07-02 05:46:56.301 eqi, eqj = i, j
2025-07-02 05:46:56.307 continue
2025-07-02 05:46:56.315 cruncher.set_seq1(ai)
2025-07-02 05:46:56.323 # computing similarity is expensive, so use the quick
2025-07-02 05:46:56.332 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:56.340 # compares by a factor of 3.
2025-07-02 05:46:56.348 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:56.355 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:56.360 # of the computation is cached by cruncher
2025-07-02 05:46:56.365 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:56.370 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:56.375 cruncher.ratio() > best_ratio:
2025-07-02 05:46:56.380 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:56.386 if best_ratio < cutoff:
2025-07-02 05:46:56.392 # no non-identical "pretty close" pair
2025-07-02 05:46:56.399 if eqi is None:
2025-07-02 05:46:56.411 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:56.419 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:56.426 return
2025-07-02 05:46:56.433 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:56.445 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:56.456 else:
2025-07-02 05:46:56.469 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:56.479 eqi = None
2025-07-02 05:46:56.486
2025-07-02 05:46:56.495 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:56.507 # identical
2025-07-02 05:46:56.517
2025-07-02 05:46:56.524 # pump out diffs from before the synch point
2025-07-02 05:46:56.532 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:56.538
2025-07-02 05:46:56.549 # do intraline marking on the synch pair
2025-07-02 05:46:56.559 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:56.567 if eqi is None:
2025-07-02 05:46:56.575 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:56.586 atags = btags = ""
2025-07-02 05:46:56.595 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:56.608 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:56.617 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:56.624 if tag == 'replace':
2025-07-02 05:46:56.630 atags += '^' * la
2025-07-02 05:46:56.635 btags += '^' * lb
2025-07-02 05:46:56.640 elif tag == 'delete':
2025-07-02 05:46:56.646 atags += '-' * la
2025-07-02 05:46:56.654 elif tag == 'insert':
2025-07-02 05:46:56.662 btags += '+' * lb
2025-07-02 05:46:56.670 elif tag == 'equal':
2025-07-02 05:46:56.678 atags += ' ' * la
2025-07-02 05:46:56.689 btags += ' ' * lb
2025-07-02 05:46:56.698 else:
2025-07-02 05:46:56.709 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:56.719 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:56.727 else:
2025-07-02 05:46:56.738 # the synch pair is identical
2025-07-02 05:46:56.750 yield '  ' + aelt
2025-07-02 05:46:56.763
2025-07-02 05:46:56.773 # pump out diffs from after the synch point
2025-07-02 05:46:56.783 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:56.791
2025-07-02 05:46:56.803 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:56.813 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:56.820
2025-07-02 05:46:56.828 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:56.836 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:56.842 alo = 97, ahi = 1101
2025-07-02 05:46:56.852 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:56.860 blo = 97, bhi = 1101
2025-07-02 05:46:56.866
2025-07-02 05:46:56.875 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:56.885 g = []
2025-07-02 05:46:56.893 if alo < ahi:
2025-07-02 05:46:56.899 if blo < bhi:
2025-07-02 05:46:56.907 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:56.918 else:
2025-07-02 05:46:56.927 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:56.941 elif blo < bhi:
2025-07-02 05:46:56.950 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:56.962
2025-07-02 05:46:56.974 >       yield from g
2025-07-02 05:46:56.986
2025-07-02 05:46:56.997 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:57.006 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:57.017
2025-07-02 05:46:57.030 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:57.043 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:57.053 alo = 97, ahi = 1101
2025-07-02 05:46:57.066 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:57.078 blo = 97, bhi = 1101
2025-07-02 05:46:57.085
2025-07-02 05:46:57.093 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:57.100 r"""
2025-07-02 05:46:57.107 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:57.115 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:57.126 synch point, and intraline difference marking is done on the
2025-07-02 05:46:57.138 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:57.149
2025-07-02 05:46:57.162 Example:
2025-07-02 05:46:57.175
2025-07-02 05:46:57.184 >>> d = Differ()
2025-07-02 05:46:57.192 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:57.200 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:57.206 >>> print(''.join(results), end="")
2025-07-02 05:46:57.212 - abcDefghiJkl
2025-07-02 05:46:57.229 + abcdefGhijkl
2025-07-02 05:46:57.247 """
2025-07-02 05:46:57.254
2025-07-02 05:46:57.266 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:57.275 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:57.283 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:57.290 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:57.296 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:57.301
2025-07-02 05:46:57.307 # search for the pair that matches best without being identical
2025-07-02 05:46:57.313 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:57.318 # on junk -- unless we have to)
2025-07-02 05:46:57.324 for j in range(blo, bhi):
2025-07-02 05:46:57.331 bj = b[j]
2025-07-02 05:46:57.341 cruncher.set_seq2(bj)
2025-07-02 05:46:57.348 for i in range(alo, ahi):
2025-07-02 05:46:57.355 ai = a[i]
2025-07-02 05:46:57.363 if ai == bj:
2025-07-02 05:46:57.373 if eqi is None:
2025-07-02 05:46:57.383 eqi, eqj = i, j
2025-07-02 05:46:57.390 continue
2025-07-02 05:46:57.401 cruncher.set_seq1(ai)
2025-07-02 05:46:57.412 # computing similarity is expensive, so use the quick
2025-07-02 05:46:57.421 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:57.432 # compares by a factor of 3.
2025-07-02 05:46:57.444 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:57.452 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:57.460 # of the computation is cached by cruncher
2025-07-02 05:46:57.469 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:57.481 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:57.490 cruncher.ratio() > best_ratio:
2025-07-02 05:46:57.498 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:57.508 if best_ratio < cutoff:
2025-07-02 05:46:57.521 # no non-identical "pretty close" pair
2025-07-02 05:46:57.530 if eqi is None:
2025-07-02 05:46:57.537 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:57.544 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:57.550 return
2025-07-02 05:46:57.559 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:57.571 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:57.584 else:
2025-07-02 05:46:57.596 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:57.607 eqi = None
2025-07-02 05:46:57.618
2025-07-02 05:46:57.629 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:57.637 # identical
2025-07-02 05:46:57.644
2025-07-02 05:46:57.650 # pump out diffs from before the synch point
2025-07-02 05:46:57.657 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:57.663
2025-07-02 05:46:57.669 # do intraline marking on the synch pair
2025-07-02 05:46:57.677 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:57.689 if eqi is None:
2025-07-02 05:46:57.702 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:57.712 atags = btags = ""
2025-07-02 05:46:57.723 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:57.735 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:57.749 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:57.762 if tag == 'replace':
2025-07-02 05:46:57.770 atags += '^' * la
2025-07-02 05:46:57.777 btags += '^' * lb
2025-07-02 05:46:57.783 elif tag == 'delete':
2025-07-02 05:46:57.790 atags += '-' * la
2025-07-02 05:46:57.796 elif tag == 'insert':
2025-07-02 05:46:57.803 btags += '+' * lb
2025-07-02 05:46:57.811 elif tag == 'equal':
2025-07-02 05:46:57.823 atags += ' ' * la
2025-07-02 05:46:57.834 btags += ' ' * lb
2025-07-02 05:46:57.839 else:
2025-07-02 05:46:57.845 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:57.850 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:57.854 else:
2025-07-02 05:46:57.867 # the synch pair is identical
2025-07-02 05:46:57.879 yield '  ' + aelt
2025-07-02 05:46:57.888
2025-07-02 05:46:57.895 # pump out diffs from after the synch point
2025-07-02 05:46:57.902 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:57.908
2025-07-02 05:46:57.914 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:57.920 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:57.926
2025-07-02 05:46:57.937 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:57.948 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:57.958 alo = 98, ahi = 1101
2025-07-02 05:46:57.972 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:57.982 blo = 98, bhi = 1101
2025-07-02 05:46:57.991
2025-07-02 05:46:57.998 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:58.009 g = []
2025-07-02 05:46:58.019 if alo < ahi:
2025-07-02 05:46:58.027 if blo < bhi:
2025-07-02 05:46:58.035 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:58.041 else:
2025-07-02 05:46:58.047 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:58.053 elif blo < bhi:
2025-07-02 05:46:58.063 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:58.071
2025-07-02 05:46:58.079 >       yield from g
2025-07-02 05:46:58.087
2025-07-02 05:46:58.099 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:58.108 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:58.114
2025-07-02 05:46:58.120 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:58.125 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:58.130 alo = 98, ahi = 1101
2025-07-02 05:46:58.136 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:58.141 blo = 98, bhi = 1101
2025-07-02 05:46:58.147
2025-07-02 05:46:58.156 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:58.168 r"""
2025-07-02 05:46:58.180 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:58.190 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:58.203 synch point, and intraline difference marking is done on the
2025-07-02 05:46:58.214 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:58.222
2025-07-02 05:46:58.229 Example:
2025-07-02 05:46:58.234
2025-07-02 05:46:58.240 >>> d = Differ()
2025-07-02 05:46:58.246 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:58.255 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:58.267 >>> print(''.join(results), end="")
2025-07-02 05:46:58.275 - abcDefghiJkl
2025-07-02 05:46:58.289 + abcdefGhijkl
2025-07-02 05:46:58.303 """
2025-07-02 05:46:58.310
2025-07-02 05:46:58.320 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:58.333 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:58.343 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:58.354 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:58.365 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:58.372
2025-07-02 05:46:58.377 # search for the pair that matches best without being identical
2025-07-02 05:46:58.382 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:58.387 # on junk -- unless we have to)
2025-07-02 05:46:58.392 for j in range(blo, bhi):
2025-07-02 05:46:58.396 bj = b[j]
2025-07-02 05:46:58.401 cruncher.set_seq2(bj)
2025-07-02 05:46:58.405 for i in range(alo, ahi):
2025-07-02 05:46:58.410 ai = a[i]
2025-07-02 05:46:58.414 if ai == bj:
2025-07-02 05:46:58.419 if eqi is None:
2025-07-02 05:46:58.428 eqi, eqj = i, j
2025-07-02 05:46:58.438 continue
2025-07-02 05:46:58.448 cruncher.set_seq1(ai)
2025-07-02 05:46:58.461 # computing similarity is expensive, so use the quick
2025-07-02 05:46:58.472 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:58.481 # compares by a factor of 3.
2025-07-02 05:46:58.489 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:58.496 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:58.503 # of the computation is cached by cruncher
2025-07-02 05:46:58.510 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:58.517 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:58.524 cruncher.ratio() > best_ratio:
2025-07-02 05:46:58.531 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:58.540 if best_ratio < cutoff:
2025-07-02 05:46:58.553 # no non-identical "pretty close" pair
2025-07-02 05:46:58.561 if eqi is None:
2025-07-02 05:46:58.568 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:58.575 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:58.584 return
2025-07-02 05:46:58.595 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:58.603 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:58.610 else:
2025-07-02 05:46:58.619 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:58.628 eqi = None
2025-07-02 05:46:58.635
2025-07-02 05:46:58.646 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:58.657 # identical
2025-07-02 05:46:58.668
2025-07-02 05:46:58.676 # pump out diffs from before the synch point
2025-07-02 05:46:58.686 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:58.699
2025-07-02 05:46:58.711 # do intraline marking on the synch pair
2025-07-02 05:46:58.720 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:58.728 if eqi is None:
2025-07-02 05:46:58.734 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:58.745 atags = btags = ""
2025-07-02 05:46:58.754 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:58.762 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:58.769 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:58.776 if tag == 'replace':
2025-07-02 05:46:58.782 atags += '^' * la
2025-07-02 05:46:58.793 btags += '^' * lb
2025-07-02 05:46:58.801 elif tag == 'delete':
2025-07-02 05:46:58.808 atags += '-' * la
2025-07-02 05:46:58.820 elif tag == 'insert':
2025-07-02 05:46:58.832 btags += '+' * lb
2025-07-02 05:46:58.843 elif tag == 'equal':
2025-07-02 05:46:58.855 atags += ' ' * la
2025-07-02 05:46:58.865 btags += ' ' * lb
2025-07-02 05:46:58.872 else:
2025-07-02 05:46:58.879 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:58.887 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:58.898 else:
2025-07-02 05:46:58.906 # the synch pair is identical
2025-07-02 05:46:58.915 yield '  ' + aelt
2025-07-02 05:46:58.924
2025-07-02 05:46:58.931 # pump out diffs from after the synch point
2025-07-02 05:46:58.941 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:58.954
2025-07-02 05:46:58.966 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:58.980 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:58.989
2025-07-02 05:46:58.997 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:59.006 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:59.014 alo = 99, ahi = 1101
2025-07-02 05:46:59.024 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:59.031 blo = 99, bhi = 1101
2025-07-02 05:46:59.038
2025-07-02 05:46:59.050 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:59.059 g = []
2025-07-02 05:46:59.068 if alo < ahi:
2025-07-02 05:46:59.077 if blo < bhi:
2025-07-02 05:46:59.085 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:59.093 else:
2025-07-02 05:46:59.106 g = self._dump('-', a, alo, ahi)
2025-07-02 05:46:59.118 elif blo < bhi:
2025-07-02 05:46:59.125 g = self._dump('+', b, blo, bhi)
2025-07-02 05:46:59.131
2025-07-02 05:46:59.136 >       yield from g
2025-07-02 05:46:59.141
2025-07-02 05:46:59.146 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:46:59.153 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:59.159
2025-07-02 05:46:59.165 self = <difflib.Differ object at [hex]>
2025-07-02 05:46:59.182 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:46:59.190 alo = 99, ahi = 1101
2025-07-02 05:46:59.199 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:46:59.206 blo = 99, bhi = 1101
2025-07-02 05:46:59.213
2025-07-02 05:46:59.219 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:46:59.225 r"""
2025-07-02 05:46:59.231 When replacing one block of lines with another, search the blocks
2025-07-02 05:46:59.237 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:46:59.244 synch point, and intraline difference marking is done on the
2025-07-02 05:46:59.250 similar pair. Lots of work, but often worth it.
2025-07-02 05:46:59.257
2025-07-02 05:46:59.266 Example:
2025-07-02 05:46:59.277
2025-07-02 05:46:59.285 >>> d = Differ()
2025-07-02 05:46:59.293 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:46:59.299 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:46:59.305 >>> print(''.join(results), end="")
2025-07-02 05:46:59.310 - abcDefghiJkl
2025-07-02 05:46:59.322 + abcdefGhijkl
2025-07-02 05:46:59.334 """
2025-07-02 05:46:59.339
2025-07-02 05:46:59.347 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:46:59.358 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:46:59.370 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:46:59.381 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:46:59.389 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:46:59.395
2025-07-02 05:46:59.400 # search for the pair that matches best without being identical
2025-07-02 05:46:59.405 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:46:59.410 # on junk -- unless we have to)
2025-07-02 05:46:59.414 for j in range(blo, bhi):
2025-07-02 05:46:59.419 bj = b[j]
2025-07-02 05:46:59.425 cruncher.set_seq2(bj)
2025-07-02 05:46:59.432 for i in range(alo, ahi):
2025-07-02 05:46:59.439 ai = a[i]
2025-07-02 05:46:59.446 if ai == bj:
2025-07-02 05:46:59.454 if eqi is None:
2025-07-02 05:46:59.466 eqi, eqj = i, j
2025-07-02 05:46:59.476 continue
2025-07-02 05:46:59.485 cruncher.set_seq1(ai)
2025-07-02 05:46:59.492 # computing similarity is expensive, so use the quick
2025-07-02 05:46:59.499 # upper bounds first -- have seen this speed up messy
2025-07-02 05:46:59.506 # compares by a factor of 3.
2025-07-02 05:46:59.514 # note that ratio() is only expensive to compute the first
2025-07-02 05:46:59.525 # time it's called on a sequence pair; the expensive part
2025-07-02 05:46:59.536 # of the computation is cached by cruncher
2025-07-02 05:46:59.547 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:46:59.557 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:46:59.568 cruncher.ratio() > best_ratio:
2025-07-02 05:46:59.583 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:46:59.590 if best_ratio < cutoff:
2025-07-02 05:46:59.599 # no non-identical "pretty close" pair
2025-07-02 05:46:59.608 if eqi is None:
2025-07-02 05:46:59.616 # no identical pair either -- treat it as a straight replace
2025-07-02 05:46:59.623 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:46:59.629 return
2025-07-02 05:46:59.635 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:46:59.641 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:46:59.647 else:
2025-07-02 05:46:59.653 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:46:59.659 eqi = None
2025-07-02 05:46:59.665
2025-07-02 05:46:59.671 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:46:59.681 # identical
2025-07-02 05:46:59.691
2025-07-02 05:46:59.700 # pump out diffs from before the synch point
2025-07-02 05:46:59.708 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:46:59.716
2025-07-02 05:46:59.722 # do intraline marking on the synch pair
2025-07-02 05:46:59.733 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:46:59.742 if eqi is None:
2025-07-02 05:46:59.753 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:46:59.765 atags = btags = ""
2025-07-02 05:46:59.774 cruncher.set_seqs(aelt, belt)
2025-07-02 05:46:59.782 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:46:59.792 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:46:59.805 if tag == 'replace':
2025-07-02 05:46:59.815 atags += '^' * la
2025-07-02 05:46:59.823 btags += '^' * lb
2025-07-02 05:46:59.831 elif tag == 'delete':
2025-07-02 05:46:59.838 atags += '-' * la
2025-07-02 05:46:59.844 elif tag == 'insert':
2025-07-02 05:46:59.850 btags += '+' * lb
2025-07-02 05:46:59.856 elif tag == 'equal':
2025-07-02 05:46:59.863 atags += ' ' * la
2025-07-02 05:46:59.875 btags += ' ' * lb
2025-07-02 05:46:59.885 else:
2025-07-02 05:46:59.897 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:46:59.909 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:46:59.919 else:
2025-07-02 05:46:59.931 # the synch pair is identical
2025-07-02 05:46:59.940 yield '  ' + aelt
2025-07-02 05:46:59.948
2025-07-02 05:46:59.955 # pump out diffs from after the synch point
2025-07-02 05:46:59.962 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:46:59.972
2025-07-02 05:46:59.980 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:46:59.988 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:46:59.994
2025-07-02 05:47:00.000 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:00.012 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:00.025 alo = 100, ahi = 1101
2025-07-02 05:47:00.036 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:00.043 blo = 100, bhi = 1101
2025-07-02 05:47:00.050
2025-07-02 05:47:00.061 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:00.070 g = []
2025-07-02 05:47:00.077 if alo < ahi:
2025-07-02 05:47:00.083 if blo < bhi:
2025-07-02 05:47:00.092 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:00.103 else:
2025-07-02 05:47:00.114 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:00.121 elif blo < bhi:
2025-07-02 05:47:00.128 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:00.136
2025-07-02 05:47:00.143 >       yield from g
2025-07-02 05:47:00.150
2025-07-02 05:47:00.156 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:00.163 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:00.172
2025-07-02 05:47:00.184 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:00.194 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:00.204 alo = 100, ahi = 1101
2025-07-02 05:47:00.213 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:00.219 blo = 100, bhi = 1101
2025-07-02 05:47:00.227
2025-07-02 05:47:00.238 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:00.248 r"""
2025-07-02 05:47:00.256 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:00.263 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:00.270 synch point, and intraline difference marking is done on the
2025-07-02 05:47:00.282 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:00.294
2025-07-02 05:47:00.301 Example:
2025-07-02 05:47:00.307
2025-07-02 05:47:00.313 >>> d = Differ()
2025-07-02 05:47:00.319 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:00.325 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:00.333 >>> print(''.join(results), end="")
2025-07-02 05:47:00.340 - abcDefghiJkl
2025-07-02 05:47:00.351 + abcdefGhijkl
2025-07-02 05:47:00.370 """
2025-07-02 05:47:00.379
2025-07-02 05:47:00.388 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:00.395 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:00.409 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:00.420 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:00.429 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:00.436
2025-07-02 05:47:00.444 # search for the pair that matches best without being identical
2025-07-02 05:47:00.452 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:00.458 # on junk -- unless we have to)
2025-07-02 05:47:00.465 for j in range(blo, bhi):
2025-07-02 05:47:00.474 bj = b[j]
2025-07-02 05:47:00.488 cruncher.set_seq2(bj)
2025-07-02 05:47:00.498 for i in range(alo, ahi):
2025-07-02 05:47:00.507 ai = a[i]
2025-07-02 05:47:00.514 if ai == bj:
2025-07-02 05:47:00.520 if eqi is None:
2025-07-02 05:47:00.529 eqi, eqj = i, j
2025-07-02 05:47:00.539 continue
2025-07-02 05:47:00.546 cruncher.set_seq1(ai)
2025-07-02 05:47:00.554 # computing similarity is expensive, so use the quick
2025-07-02 05:47:00.561 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:00.567 # compares by a factor of 3.
2025-07-02 05:47:00.575 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:00.582 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:00.593 # of the computation is cached by cruncher
2025-07-02 05:47:00.601 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:00.608 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:00.614 cruncher.ratio() > best_ratio:
2025-07-02 05:47:00.621 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:00.627 if best_ratio < cutoff:
2025-07-02 05:47:00.640 # no non-identical "pretty close" pair
2025-07-02 05:47:00.649 if eqi is None:
2025-07-02 05:47:00.657 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:00.664 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:00.670 return
2025-07-02 05:47:00.676 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:00.682 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:00.693 else:
2025-07-02 05:47:00.702 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:00.710 eqi = None
2025-07-02 05:47:00.719
2025-07-02 05:47:00.729 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:00.735 # identical
2025-07-02 05:47:00.742
2025-07-02 05:47:00.747 # pump out diffs from before the synch point
2025-07-02 05:47:00.752 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:00.756
2025-07-02 05:47:00.761 # do intraline marking on the synch pair
2025-07-02 05:47:00.765 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:00.770 if eqi is None:
2025-07-02 05:47:00.775 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:00.779 atags = btags = ""
2025-07-02 05:47:00.784 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:00.789 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:00.794 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:00.798 if tag == 'replace':
2025-07-02 05:47:00.803 atags += '^' * la
2025-07-02 05:47:00.808 btags += '^' * lb
2025-07-02 05:47:00.813 elif tag == 'delete':
2025-07-02 05:47:00.818 atags += '-' * la
2025-07-02 05:47:00.823 elif tag == 'insert':
2025-07-02 05:47:00.829 btags += '+' * lb
2025-07-02 05:47:00.836 elif tag == 'equal':
2025-07-02 05:47:00.847 atags += ' ' * la
2025-07-02 05:47:00.855 btags += ' ' * lb
2025-07-02 05:47:00.863 else:
2025-07-02 05:47:00.871 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:00.877 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:00.887 else:
2025-07-02 05:47:00.899 # the synch pair is identical
2025-07-02 05:47:00.908 yield '  ' + aelt
2025-07-02 05:47:00.915
2025-07-02 05:47:00.922 # pump out diffs from after the synch point
2025-07-02 05:47:00.935 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:00.946
2025-07-02 05:47:00.956 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:00.967 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:00.980
2025-07-02 05:47:00.993 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:01.007 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:01.019 alo = 101, ahi = 1101
2025-07-02 05:47:01.029 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:01.040 blo = 101, bhi = 1101
2025-07-02 05:47:01.053
2025-07-02 05:47:01.063 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:01.074 g = []
2025-07-02 05:47:01.086 if alo < ahi:
2025-07-02 05:47:01.099 if blo < bhi:
2025-07-02 05:47:01.109 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:01.115 else:
2025-07-02 05:47:01.126 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:01.137 elif blo < bhi:
2025-07-02 05:47:01.144 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:01.151
2025-07-02 05:47:01.159 >       yield from g
2025-07-02 05:47:01.170
2025-07-02 05:47:01.178 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:01.187 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:01.196
2025-07-02 05:47:01.203 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:01.218 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:01.226 alo = 101, ahi = 1101
2025-07-02 05:47:01.233 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:01.239 blo = 101, bhi = 1101
2025-07-02 05:47:01.246
2025-07-02 05:47:01.253 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:01.260 r"""
2025-07-02 05:47:01.267 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:01.275 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:01.283 synch point, and intraline difference marking is done on the
2025-07-02 05:47:01.289 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:01.295
2025-07-02 05:47:01.300 Example:
2025-07-02 05:47:01.307
2025-07-02 05:47:01.316 >>> d = Differ()
2025-07-02 05:47:01.323 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:01.329 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:01.335 >>> print(''.join(results), end="")
2025-07-02 05:47:01.341 - abcDefghiJkl
2025-07-02 05:47:01.354 + abcdefGhijkl
2025-07-02 05:47:01.366 """
2025-07-02 05:47:01.377
2025-07-02 05:47:01.389 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:01.398 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:01.408 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:01.419 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:01.428 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:01.436
2025-07-02 05:47:01.444 # search for the pair that matches best without being identical
2025-07-02 05:47:01.451 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:01.458 # on junk -- unless we have to)
2025-07-02 05:47:01.463 for j in range(blo, bhi):
2025-07-02 05:47:01.468 bj = b[j]
2025-07-02 05:47:01.473 cruncher.set_seq2(bj)
2025-07-02 05:47:01.479 for i in range(alo, ahi):
2025-07-02 05:47:01.485 ai = a[i]
2025-07-02 05:47:01.491 if ai == bj:
2025-07-02 05:47:01.500 if eqi is None:
2025-07-02 05:47:01.510 eqi, eqj = i, j
2025-07-02 05:47:01.520 continue
2025-07-02 05:47:01.529 cruncher.set_seq1(ai)
2025-07-02 05:47:01.537 # computing similarity is expensive, so use the quick
2025-07-02 05:47:01.548 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:01.561 # compares by a factor of 3.
2025-07-02 05:47:01.571 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:01.584 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:01.597 # of the computation is cached by cruncher
2025-07-02 05:47:01.607 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:01.621 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:01.633 cruncher.ratio() > best_ratio:
2025-07-02 05:47:01.647 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:01.656 if best_ratio < cutoff:
2025-07-02 05:47:01.663 # no non-identical "pretty close" pair
2025-07-02 05:47:01.672 if eqi is None:
2025-07-02 05:47:01.685 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:01.696 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:01.704 return
2025-07-02 05:47:01.712 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:01.719 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:01.724 else:
2025-07-02 05:47:01.731 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:01.743 eqi = None
2025-07-02 05:47:01.754
2025-07-02 05:47:01.763 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:01.772 # identical
2025-07-02 05:47:01.779
2025-07-02 05:47:01.787 # pump out diffs from before the synch point
2025-07-02 05:47:01.794 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:01.801
2025-07-02 05:47:01.808 # do intraline marking on the synch pair
2025-07-02 05:47:01.815 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:01.822 if eqi is None:
2025-07-02 05:47:01.829 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:01.841 atags = btags = ""
2025-07-02 05:47:01.851 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:01.863 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:01.872 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:01.880 if tag == 'replace':
2025-07-02 05:47:01.889 atags += '^' * la
2025-07-02 05:47:01.901 btags += '^' * lb
2025-07-02 05:47:01.912 elif tag == 'delete':
2025-07-02 05:47:01.921 atags += '-' * la
2025-07-02 05:47:01.928 elif tag == 'insert':
2025-07-02 05:47:01.935 btags += '+' * lb
2025-07-02 05:47:01.947 elif tag == 'equal':
2025-07-02 05:47:01.959 atags += ' ' * la
2025-07-02 05:47:01.972 btags += ' ' * lb
2025-07-02 05:47:01.982 else:
2025-07-02 05:47:01.990 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:01.998 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:02.007 else:
2025-07-02 05:47:02.019 # the synch pair is identical
2025-07-02 05:47:02.027 yield '  ' + aelt
2025-07-02 05:47:02.035
2025-07-02 05:47:02.041 # pump out diffs from after the synch point
2025-07-02 05:47:02.046 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:02.054
2025-07-02 05:47:02.065 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:02.075 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:02.082
2025-07-02 05:47:02.087 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:02.092 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:02.099 alo = 102, ahi = 1101
2025-07-02 05:47:02.108 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:02.117 blo = 102, bhi = 1101
2025-07-02 05:47:02.126
2025-07-02 05:47:02.138 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:02.146 g = []
2025-07-02 05:47:02.153 if alo < ahi:
2025-07-02 05:47:02.159 if blo < bhi:
2025-07-02 05:47:02.166 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:02.171 else:
2025-07-02 05:47:02.176 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:02.182 elif blo < bhi:
2025-07-02 05:47:02.188 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:02.194
2025-07-02 05:47:02.200 >       yield from g
2025-07-02 05:47:02.206
2025-07-02 05:47:02.215 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:02.222 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:02.230
2025-07-02 05:47:02.239 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:02.253 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:02.264 alo = 102, ahi = 1101
2025-07-02 05:47:02.276 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:02.288 blo = 102, bhi = 1101
2025-07-02 05:47:02.299
2025-07-02 05:47:02.314 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:02.325 r"""
2025-07-02 05:47:02.333 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:02.345 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:02.355 synch point, and intraline difference marking is done on the
2025-07-02 05:47:02.366 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:02.374
2025-07-02 05:47:02.381 Example:
2025-07-02 05:47:02.387
2025-07-02 05:47:02.394 >>> d = Differ()
2025-07-02 05:47:02.401 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:02.407 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:02.414 >>> print(''.join(results), end="")
2025-07-02 05:47:02.425 - abcDefghiJkl
2025-07-02 05:47:02.442 + abcdefGhijkl
2025-07-02 05:47:02.454 """
2025-07-02 05:47:02.459
2025-07-02 05:47:02.464 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:02.469 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:02.475 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:02.481 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:02.487 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:02.492
2025-07-02 05:47:02.498 # search for the pair that matches best without being identical
2025-07-02 05:47:02.505 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:02.510 # on junk -- unless we have to)
2025-07-02 05:47:02.516 for j in range(blo, bhi):
2025-07-02 05:47:02.523 bj = b[j]
2025-07-02 05:47:02.530 cruncher.set_seq2(bj)
2025-07-02 05:47:02.539 for i in range(alo, ahi):
2025-07-02 05:47:02.548 ai = a[i]
2025-07-02 05:47:02.561 if ai == bj:
2025-07-02 05:47:02.571 if eqi is None:
2025-07-02 05:47:02.579 eqi, eqj = i, j
2025-07-02 05:47:02.584 continue
2025-07-02 05:47:02.590 cruncher.set_seq1(ai)
2025-07-02 05:47:02.595 # computing similarity is expensive, so use the quick
2025-07-02 05:47:02.602 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:02.607 # compares by a factor of 3.
2025-07-02 05:47:02.614 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:02.620 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:02.626 # of the computation is cached by cruncher
2025-07-02 05:47:02.633 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:02.640 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:02.647 cruncher.ratio() > best_ratio:
2025-07-02 05:47:02.654 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:02.661 if best_ratio < cutoff:
2025-07-02 05:47:02.668 # no non-identical "pretty close" pair
2025-07-02 05:47:02.674 if eqi is None:
2025-07-02 05:47:02.686 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:02.700 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:02.712 return
2025-07-02 05:47:02.726 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:02.735 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:02.743 else:
2025-07-02 05:47:02.750 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:02.760 eqi = None
2025-07-02 05:47:02.773
2025-07-02 05:47:02.783 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:02.790 # identical
2025-07-02 05:47:02.796
2025-07-02 05:47:02.802 # pump out diffs from before the synch point
2025-07-02 05:47:02.807 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:02.813
2025-07-02 05:47:02.819 # do intraline marking on the synch pair
2025-07-02 05:47:02.827 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:02.835 if eqi is None:
2025-07-02 05:47:02.847 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:02.857 atags = btags = ""
2025-07-02 05:47:02.865 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:02.875 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:02.882 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:02.891 if tag == 'replace':
2025-07-02 05:47:02.901 atags += '^' * la
2025-07-02 05:47:02.908 btags += '^' * lb
2025-07-02 05:47:02.915 elif tag == 'delete':
2025-07-02 05:47:02.921 atags += '-' * la
2025-07-02 05:47:02.926 elif tag == 'insert':
2025-07-02 05:47:02.931 btags += '+' * lb
2025-07-02 05:47:02.936 elif tag == 'equal':
2025-07-02 05:47:02.942 atags += ' ' * la
2025-07-02 05:47:02.948 btags += ' ' * lb
2025-07-02 05:47:02.955 else:
2025-07-02 05:47:02.962 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:02.972 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:02.981 else:
2025-07-02 05:47:02.991 # the synch pair is identical
2025-07-02 05:47:03.001 yield '  ' + aelt
2025-07-02 05:47:03.011
2025-07-02 05:47:03.020 # pump out diffs from after the synch point
2025-07-02 05:47:03.028 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:03.033
2025-07-02 05:47:03.038 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:03.044 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:03.050
2025-07-02 05:47:03.056 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:03.064 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:03.071 alo = 103, ahi = 1101
2025-07-02 05:47:03.079 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:03.085 blo = 103, bhi = 1101
2025-07-02 05:47:03.091
2025-07-02 05:47:03.097 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:03.105 g = []
2025-07-02 05:47:03.115 if alo < ahi:
2025-07-02 05:47:03.122 if blo < bhi:
2025-07-02 05:47:03.128 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:03.134 else:
2025-07-02 05:47:03.140 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:03.146 elif blo < bhi:
2025-07-02 05:47:03.158 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:03.169
2025-07-02 05:47:03.177 >       yield from g
2025-07-02 05:47:03.183
2025-07-02 05:47:03.188 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:03.193 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:03.201
2025-07-02 05:47:03.214 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:03.223 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:03.229 alo = 103, ahi = 1101
2025-07-02 05:47:03.237 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:03.243 blo = 103, bhi = 1101
2025-07-02 05:47:03.250
2025-07-02 05:47:03.257 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:03.263 r"""
2025-07-02 05:47:03.269 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:03.275 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:03.280 synch point, and intraline difference marking is done on the
2025-07-02 05:47:03.285 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:03.291
2025-07-02 05:47:03.297 Example:
2025-07-02 05:47:03.303
2025-07-02 05:47:03.311 >>> d = Differ()
2025-07-02 05:47:03.323 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:03.331 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:03.338 >>> print(''.join(results), end="")
2025-07-02 05:47:03.343 - abcDefghiJkl
2025-07-02 05:47:03.353 + abcdefGhijkl
2025-07-02 05:47:03.363 """
2025-07-02 05:47:03.368
2025-07-02 05:47:03.374 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:03.380 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:03.386 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:03.393 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:03.401 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:03.409
2025-07-02 05:47:03.417 # search for the pair that matches best without being identical
2025-07-02 05:47:03.424 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:03.431 # on junk -- unless we have to)
2025-07-02 05:47:03.438 for j in range(blo, bhi):
2025-07-02 05:47:03.448 bj = b[j]
2025-07-02 05:47:03.456 cruncher.set_seq2(bj)
2025-07-02 05:47:03.463 for i in range(alo, ahi):
2025-07-02 05:47:03.470 ai = a[i]
2025-07-02 05:47:03.479 if ai == bj:
2025-07-02 05:47:03.490 if eqi is None:
2025-07-02 05:47:03.498 eqi, eqj = i, j
2025-07-02 05:47:03.510 continue
2025-07-02 05:47:03.522 cruncher.set_seq1(ai)
2025-07-02 05:47:03.537 # computing similarity is expensive, so use the quick
2025-07-02 05:47:03.548 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:03.556 # compares by a factor of 3.
2025-07-02 05:47:03.564 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:03.572 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:03.580 # of the computation is cached by cruncher
2025-07-02 05:47:03.587 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:03.594 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:03.602 cruncher.ratio() > best_ratio:
2025-07-02 05:47:03.610 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:03.622 if best_ratio < cutoff:
2025-07-02 05:47:03.632 # no non-identical "pretty close" pair
2025-07-02 05:47:03.640 if eqi is None:
2025-07-02 05:47:03.646 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:03.657 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:03.666 return
2025-07-02 05:47:03.673 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:03.679 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:03.685 else:
2025-07-02 05:47:03.691 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:03.699 eqi = None
2025-07-02 05:47:03.713
2025-07-02 05:47:03.723 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:03.730 # identical
2025-07-02 05:47:03.740
2025-07-02 05:47:03.752 # pump out diffs from before the synch point
2025-07-02 05:47:03.761 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:03.772
2025-07-02 05:47:03.783 # do intraline marking on the synch pair
2025-07-02 05:47:03.791 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:03.797 if eqi is None:
2025-07-02 05:47:03.803 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:03.808 atags = btags = ""
2025-07-02 05:47:03.813 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:03.819 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:03.824 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:03.836 if tag == 'replace':
2025-07-02 05:47:03.846 atags += '^' * la
2025-07-02 05:47:03.853 btags += '^' * lb
2025-07-02 05:47:03.860 elif tag == 'delete':
2025-07-02 05:47:03.871 atags += '-' * la
2025-07-02 05:47:03.883 elif tag == 'insert':
2025-07-02 05:47:03.893 btags += '+' * lb
2025-07-02 05:47:03.901 elif tag == 'equal':
2025-07-02 05:47:03.908 atags += ' ' * la
2025-07-02 05:47:03.914 btags += ' ' * lb
2025-07-02 05:47:03.920 else:
2025-07-02 05:47:03.926 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:03.937 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:03.950 else:
2025-07-02 05:47:03.961 # the synch pair is identical
2025-07-02 05:47:03.971 yield '  ' + aelt
2025-07-02 05:47:03.979
2025-07-02 05:47:03.993 # pump out diffs from after the synch point
2025-07-02 05:47:04.005 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:04.019
2025-07-02 05:47:04.028 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:04.041 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:04.051
2025-07-02 05:47:04.059 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:04.069 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:04.076 alo = 104, ahi = 1101
2025-07-02 05:47:04.083 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:04.091 blo = 104, bhi = 1101
2025-07-02 05:47:04.103
2025-07-02 05:47:04.112 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:04.124 g = []
2025-07-02 05:47:04.137 if alo < ahi:
2025-07-02 05:47:04.148 if blo < bhi:
2025-07-02 05:47:04.159 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:04.169 else:
2025-07-02 05:47:04.180 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:04.190 elif blo < bhi:
2025-07-02 05:47:04.198 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:04.210
2025-07-02 05:47:04.222 >       yield from g
2025-07-02 05:47:04.231
2025-07-02 05:47:04.242 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:04.254 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:04.266
2025-07-02 05:47:04.277 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:04.288 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:04.301 alo = 104, ahi = 1101
2025-07-02 05:47:04.315 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:04.326 blo = 104, bhi = 1101
2025-07-02 05:47:04.334
2025-07-02 05:47:04.341 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:04.348 r"""
2025-07-02 05:47:04.356 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:04.364 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:04.372 synch point, and intraline difference marking is done on the
2025-07-02 05:47:04.378 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:04.384
2025-07-02 05:47:04.392 Example:
2025-07-02 05:47:04.405
2025-07-02 05:47:04.417 >>> d = Differ()
2025-07-02 05:47:04.426 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:04.436 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:04.445 >>> print(''.join(results), end="")
2025-07-02 05:47:04.454 - abcDefghiJkl
2025-07-02 05:47:04.471 + abcdefGhijkl
2025-07-02 05:47:04.484 """
2025-07-02 05:47:04.490
2025-07-02 05:47:04.497 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:04.503 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:04.510 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:04.519 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:04.529 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:04.536
2025-07-02 05:47:04.549 # search for the pair that matches best without being identical
2025-07-02 05:47:04.557 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:04.564 # on junk -- unless we have to)
2025-07-02 05:47:04.572 for j in range(blo, bhi):
2025-07-02 05:47:04.581 bj = b[j]
2025-07-02 05:47:04.592 cruncher.set_seq2(bj)
2025-07-02 05:47:04.602 for i in range(alo, ahi):
2025-07-02 05:47:04.611 ai = a[i]
2025-07-02 05:47:04.620 if ai == bj:
2025-07-02 05:47:04.629 if eqi is None:
2025-07-02 05:47:04.636 eqi, eqj = i, j
2025-07-02 05:47:04.643 continue
2025-07-02 05:47:04.650 cruncher.set_seq1(ai)
2025-07-02 05:47:04.656 # computing similarity is expensive, so use the quick
2025-07-02 05:47:04.663 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:04.669 # compares by a factor of 3.
2025-07-02 05:47:04.674 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:04.679 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:04.688 # of the computation is cached by cruncher
2025-07-02 05:47:04.699 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:04.708 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:04.719 cruncher.ratio() > best_ratio:
2025-07-02 05:47:04.734 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:04.747 if best_ratio < cutoff:
2025-07-02 05:47:04.759 # no non-identical "pretty close" pair
2025-07-02 05:47:04.770 if eqi is None:
2025-07-02 05:47:04.781 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:04.790 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:04.802 return
2025-07-02 05:47:04.815 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:04.827 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:04.840 else:
2025-07-02 05:47:04.855 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:04.865 eqi = None
2025-07-02 05:47:04.876
2025-07-02 05:47:04.889 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:04.902 # identical
2025-07-02 05:47:04.913
2025-07-02 05:47:04.921 # pump out diffs from before the synch point
2025-07-02 05:47:04.928 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:04.934
2025-07-02 05:47:04.940 # do intraline marking on the synch pair
2025-07-02 05:47:04.946 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:04.952 if eqi is None:
2025-07-02 05:47:04.959 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:04.966 atags = btags = ""
2025-07-02 05:47:04.978 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:04.987 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:05.001 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:05.015 if tag == 'replace':
2025-07-02 05:47:05.025 atags += '^' * la
2025-07-02 05:47:05.035 btags += '^' * lb
2025-07-02 05:47:05.048 elif tag == 'delete':
2025-07-02 05:47:05.061 atags += '-' * la
2025-07-02 05:47:05.073 elif tag == 'insert':
2025-07-02 05:47:05.083 btags += '+' * lb
2025-07-02 05:47:05.092 elif tag == 'equal':
2025-07-02 05:47:05.102 atags += ' ' * la
2025-07-02 05:47:05.112 btags += ' ' * lb
2025-07-02 05:47:05.119 else:
2025-07-02 05:47:05.123 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:05.128 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:05.133 else:
2025-07-02 05:47:05.138 # the synch pair is identical
2025-07-02 05:47:05.142 yield '  ' + aelt
2025-07-02 05:47:05.148
2025-07-02 05:47:05.160 # pump out diffs from after the synch point
2025-07-02 05:47:05.169 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:05.175
2025-07-02 05:47:05.184 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:05.194 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:05.202
2025-07-02 05:47:05.209 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:05.217 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:05.224 alo = 105, ahi = 1101
2025-07-02 05:47:05.234 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:05.248 blo = 105, bhi = 1101
2025-07-02 05:47:05.261
2025-07-02 05:47:05.274 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:05.283 g = []
2025-07-02 05:47:05.291 if alo < ahi:
2025-07-02 05:47:05.298 if blo < bhi:
2025-07-02 05:47:05.305 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:05.316 else:
2025-07-02 05:47:05.327 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:05.338 elif blo < bhi:
2025-07-02 05:47:05.345 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:05.352
2025-07-02 05:47:05.358 >       yield from g
2025-07-02 05:47:05.364
2025-07-02 05:47:05.370 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:05.383 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:05.390
2025-07-02 05:47:05.397 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:05.406 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:05.412 alo = 105, ahi = 1101
2025-07-02 05:47:05.423 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:05.431 blo = 105, bhi = 1101
2025-07-02 05:47:05.438
2025-07-02 05:47:05.445 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:05.457 r"""
2025-07-02 05:47:05.470 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:05.482 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:05.491 synch point, and intraline difference marking is done on the
2025-07-02 05:47:05.502 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:05.514
2025-07-02 05:47:05.524 Example:
2025-07-02 05:47:05.536
2025-07-02 05:47:05.546 >>> d = Differ()
2025-07-02 05:47:05.556 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:05.564 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:05.571 >>> print(''.join(results), end="")
2025-07-02 05:47:05.578 - abcDefghiJkl
2025-07-02 05:47:05.590 + abcdefGhijkl
2025-07-02 05:47:05.602 """
2025-07-02 05:47:05.608
2025-07-02 05:47:05.614 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:05.620 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:05.626 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:05.637 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:05.646 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:05.659
2025-07-02 05:47:05.672 # search for the pair that matches best without being identical
2025-07-02 05:47:05.682 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:05.692 # on junk -- unless we have to)
2025-07-02 05:47:05.703 for j in range(blo, bhi):
2025-07-02 05:47:05.713 bj = b[j]
2025-07-02 05:47:05.727 cruncher.set_seq2(bj)
2025-07-02 05:47:05.738 for i in range(alo, ahi):
2025-07-02 05:47:05.745 ai = a[i]
2025-07-02 05:47:05.751 if ai == bj:
2025-07-02 05:47:05.757 if eqi is None:
2025-07-02 05:47:05.763 eqi, eqj = i, j
2025-07-02 05:47:05.769 continue
2025-07-02 05:47:05.775 cruncher.set_seq1(ai)
2025-07-02 05:47:05.783 # computing similarity is expensive, so use the quick
2025-07-02 05:47:05.798 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:05.808 # compares by a factor of 3.
2025-07-02 05:47:05.816 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:05.823 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:05.829 # of the computation is cached by cruncher
2025-07-02 05:47:05.834 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:05.839 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:05.846 cruncher.ratio() > best_ratio:
2025-07-02 05:47:05.857 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:05.866 if best_ratio < cutoff:
2025-07-02 05:47:05.873 # no non-identical "pretty close" pair
2025-07-02 05:47:05.879 if eqi is None:
2025-07-02 05:47:05.884 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:05.889 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:05.894 return
2025-07-02 05:47:05.899 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:05.904 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:05.910 else:
2025-07-02 05:47:05.920 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:05.929 eqi = None
2025-07-02 05:47:05.936
2025-07-02 05:47:05.942 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:05.953 # identical
2025-07-02 05:47:05.963
2025-07-02 05:47:05.971 # pump out diffs from before the synch point
2025-07-02 05:47:05.979 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:05.985
2025-07-02 05:47:05.996 # do intraline marking on the synch pair
2025-07-02 05:47:06.005 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:06.013 if eqi is None:
2025-07-02 05:47:06.020 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:06.026 atags = btags = ""
2025-07-02 05:47:06.037 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:06.048 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:06.060 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:06.072 if tag == 'replace':
2025-07-02 05:47:06.083 atags += '^' * la
2025-07-02 05:47:06.093 btags += '^' * lb
2025-07-02 05:47:06.104 elif tag == 'delete':
2025-07-02 05:47:06.115 atags += '-' * la
2025-07-02 05:47:06.126 elif tag == 'insert':
2025-07-02 05:47:06.136 btags += '+' * lb
2025-07-02 05:47:06.144 elif tag == 'equal':
2025-07-02 05:47:06.153 atags += ' ' * la
2025-07-02 05:47:06.161 btags += ' ' * lb
2025-07-02 05:47:06.168 else:
2025-07-02 05:47:06.175 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:06.186 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:06.198 else:
2025-07-02 05:47:06.216 # the synch pair is identical
2025-07-02 05:47:06.224 yield '  ' + aelt
2025-07-02 05:47:06.231
2025-07-02 05:47:06.240 # pump out diffs from after the synch point
2025-07-02 05:47:06.251 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:06.259
2025-07-02 05:47:06.266 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:06.273 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:06.279
2025-07-02 05:47:06.285 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:06.295 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:06.306 alo = 106, ahi = 1101
2025-07-02 05:47:06.317 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:06.330 blo = 106, bhi = 1101
2025-07-02 05:47:06.341
2025-07-02 05:47:06.354 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:06.362 g = []
2025-07-02 05:47:06.372 if alo < ahi:
2025-07-02 05:47:06.385 if blo < bhi:
2025-07-02 05:47:06.395 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:06.406 else:
2025-07-02 05:47:06.414 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:06.421 elif blo < bhi:
2025-07-02 05:47:06.426 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:06.432
2025-07-02 05:47:06.439 >       yield from g
2025-07-02 05:47:06.451
2025-07-02 05:47:06.460 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:06.468 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:06.475
2025-07-02 05:47:06.481 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:06.490 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:06.502 alo = 106, ahi = 1101
2025-07-02 05:47:06.512 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:06.518 blo = 106, bhi = 1101
2025-07-02 05:47:06.525
2025-07-02 05:47:06.532 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:06.539 r"""
2025-07-02 05:47:06.549 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:06.558 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:06.568 synch point, and intraline difference marking is done on the
2025-07-02 05:47:06.579 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:06.587
2025-07-02 05:47:06.595 Example:
2025-07-02 05:47:06.603
2025-07-02 05:47:06.613 >>> d = Differ()
2025-07-02 05:47:06.624 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:06.637 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:06.649 >>> print(''.join(results), end="")
2025-07-02 05:47:06.660 - abcDefghiJkl
2025-07-02 05:47:06.681 + abcdefGhijkl
2025-07-02 05:47:06.700 """
2025-07-02 05:47:06.710
2025-07-02 05:47:06.724 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:06.736 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:06.746 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:06.758 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:06.771 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:06.782
2025-07-02 05:47:06.793 # search for the pair that matches best without being identical
2025-07-02 05:47:06.805 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:06.819 # on junk -- unless we have to)
2025-07-02 05:47:06.829 for j in range(blo, bhi):
2025-07-02 05:47:06.838 bj = b[j]
2025-07-02 05:47:06.847 cruncher.set_seq2(bj)
2025-07-02 05:47:06.858 for i in range(alo, ahi):
2025-07-02 05:47:06.868 ai = a[i]
2025-07-02 05:47:06.876 if ai == bj:
2025-07-02 05:47:06.882 if eqi is None:
2025-07-02 05:47:06.893 eqi, eqj = i, j
2025-07-02 05:47:06.903 continue
2025-07-02 05:47:06.911 cruncher.set_seq1(ai)
2025-07-02 05:47:06.918 # computing similarity is expensive, so use the quick
2025-07-02 05:47:06.927 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:06.939 # compares by a factor of 3.
2025-07-02 05:47:06.948 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:06.957 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:06.965 # of the computation is cached by cruncher
2025-07-02 05:47:06.972 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:06.979 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:06.985 cruncher.ratio() > best_ratio:
2025-07-02 05:47:06.991 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:06.997 if best_ratio < cutoff:
2025-07-02 05:47:07.013 # no non-identical "pretty close" pair
2025-07-02 05:47:07.024 if eqi is None:
2025-07-02 05:47:07.033 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:07.041 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:07.047 return
2025-07-02 05:47:07.060 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:07.069 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:07.076 else:
2025-07-02 05:47:07.083 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:07.093 eqi = None
2025-07-02 05:47:07.101
2025-07-02 05:47:07.109 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:07.118 # identical
2025-07-02 05:47:07.127
2025-07-02 05:47:07.134 # pump out diffs from before the synch point
2025-07-02 05:47:07.146 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:07.156
2025-07-02 05:47:07.165 # do intraline marking on the synch pair
2025-07-02 05:47:07.172 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:07.178 if eqi is None:
2025-07-02 05:47:07.185 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:07.191 atags = btags = ""
2025-07-02 05:47:07.197 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:07.203 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:07.209 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:07.220 if tag == 'replace':
2025-07-02 05:47:07.230 atags += '^' * la
2025-07-02 05:47:07.238 btags += '^' * lb
2025-07-02 05:47:07.245 elif tag == 'delete':
2025-07-02 05:47:07.252 atags += '-' * la
2025-07-02 05:47:07.259 elif tag == 'insert':
2025-07-02 05:47:07.266 btags += '+' * lb
2025-07-02 05:47:07.275 elif tag == 'equal':
2025-07-02 05:47:07.287 atags += ' ' * la
2025-07-02 05:47:07.297 btags += ' ' * lb
2025-07-02 05:47:07.308 else:
2025-07-02 05:47:07.316 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:07.324 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:07.331 else:
2025-07-02 05:47:07.338 # the synch pair is identical
2025-07-02 05:47:07.347 yield '  ' + aelt
2025-07-02 05:47:07.357
2025-07-02 05:47:07.364 # pump out diffs from after the synch point
2025-07-02 05:47:07.371 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:07.378
2025-07-02 05:47:07.385 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:07.391 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:07.398
2025-07-02 05:47:07.407 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:07.418 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:07.428 alo = 107, ahi = 1101
2025-07-02 05:47:07.435 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:07.441 blo = 107, bhi = 1101
2025-07-02 05:47:07.446
2025-07-02 05:47:07.454 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:07.463 g = []
2025-07-02 05:47:07.474 if alo < ahi:
2025-07-02 05:47:07.484 if blo < bhi:
2025-07-02 05:47:07.490 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:07.502 else:
2025-07-02 05:47:07.512 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:07.521 elif blo < bhi:
2025-07-02 05:47:07.529 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:07.535
2025-07-02 05:47:07.541 >       yield from g
2025-07-02 05:47:07.547
2025-07-02 05:47:07.553 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:07.559 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:07.566
2025-07-02 05:47:07.576 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:07.588 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:07.596 alo = 107, ahi = 1101
2025-07-02 05:47:07.604 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:07.612 blo = 107, bhi = 1101
2025-07-02 05:47:07.618
2025-07-02 05:47:07.623 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:07.629 r"""
2025-07-02 05:47:07.635 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:07.641 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:07.647 synch point, and intraline difference marking is done on the
2025-07-02 05:47:07.653 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:07.658
2025-07-02 05:47:07.664 Example:
2025-07-02 05:47:07.670
2025-07-02 05:47:07.676 >>> d = Differ()
2025-07-02 05:47:07.682 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:07.689 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:07.696 >>> print(''.join(results), end="")
2025-07-02 05:47:07.702 - abcDefghiJkl
2025-07-02 05:47:07.722 + abcdefGhijkl
2025-07-02 05:47:07.736 """
2025-07-02 05:47:07.742
2025-07-02 05:47:07.750 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:07.761 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:07.774 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:07.784 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:07.791 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:07.798
2025-07-02 05:47:07.807 # search for the pair that matches best without being identical
2025-07-02 05:47:07.818 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:07.828 # on junk -- unless we have to)
2025-07-02 05:47:07.835 for j in range(blo, bhi):
2025-07-02 05:47:07.843 bj = b[j]
2025-07-02 05:47:07.851 cruncher.set_seq2(bj)
2025-07-02 05:47:07.862 for i in range(alo, ahi):
2025-07-02 05:47:07.871 ai = a[i]
2025-07-02 05:47:07.877 if ai == bj:
2025-07-02 05:47:07.883 if eqi is None:
2025-07-02 05:47:07.888 eqi, eqj = i, j
2025-07-02 05:47:07.893 continue
2025-07-02 05:47:07.908 cruncher.set_seq1(ai)
2025-07-02 05:47:07.918 # computing similarity is expensive, so use the quick
2025-07-02 05:47:07.927 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:07.934 # compares by a factor of 3.
2025-07-02 05:47:07.940 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:07.946 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:07.951 # of the computation is cached by cruncher
2025-07-02 05:47:07.956 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:07.961 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:07.967 cruncher.ratio() > best_ratio:
2025-07-02 05:47:07.972 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:07.977 if best_ratio < cutoff:
2025-07-02 05:47:07.986 # no non-identical "pretty close" pair
2025-07-02 05:47:07.999 if eqi is None:
2025-07-02 05:47:08.010 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:08.018 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:08.025 return
2025-07-02 05:47:08.037 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:08.049 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:08.059 else:
2025-07-02 05:47:08.071 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:08.079 eqi = None
2025-07-02 05:47:08.087
2025-07-02 05:47:08.096 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:08.107 # identical
2025-07-02 05:47:08.116
2025-07-02 05:47:08.123 # pump out diffs from before the synch point
2025-07-02 05:47:08.130 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:08.142
2025-07-02 05:47:08.152 # do intraline marking on the synch pair
2025-07-02 05:47:08.160 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:08.168 if eqi is None:
2025-07-02 05:47:08.174 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:08.179 atags = btags = ""
2025-07-02 05:47:08.187 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:08.198 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:08.206 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:08.213 if tag == 'replace':
2025-07-02 05:47:08.220 atags += '^' * la
2025-07-02 05:47:08.227 btags += '^' * lb
2025-07-02 05:47:08.234 elif tag == 'delete':
2025-07-02 05:47:08.247 atags += '-' * la
2025-07-02 05:47:08.253 elif tag == 'insert':
2025-07-02 05:47:08.259 btags += '+' * lb
2025-07-02 05:47:08.265 elif tag == 'equal':
2025-07-02 05:47:08.272 atags += ' ' * la
2025-07-02 05:47:08.279 btags += ' ' * lb
2025-07-02 05:47:08.287 else:
2025-07-02 05:47:08.298 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:08.309 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:08.323 else:
2025-07-02 05:47:08.335 # the synch pair is identical
2025-07-02 05:47:08.343 yield '  ' + aelt
2025-07-02 05:47:08.350
2025-07-02 05:47:08.357 # pump out diffs from after the synch point
2025-07-02 05:47:08.364 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:08.371
2025-07-02 05:47:08.378 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:08.389 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:08.397
2025-07-02 05:47:08.406 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:08.413 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:08.418 alo = 108, ahi = 1101
2025-07-02 05:47:08.425 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:08.432 blo = 108, bhi = 1101
2025-07-02 05:47:08.438
2025-07-02 05:47:08.445 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:08.454 g = []
2025-07-02 05:47:08.460 if alo < ahi:
2025-07-02 05:47:08.466 if blo < bhi:
2025-07-02 05:47:08.471 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:08.476 else:
2025-07-02 05:47:08.481 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:08.486 elif blo < bhi:
2025-07-02 05:47:08.491 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:08.498
2025-07-02 05:47:08.509 >       yield from g
2025-07-02 05:47:08.517
2025-07-02 05:47:08.524 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:08.532 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:08.538
2025-07-02 05:47:08.545 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:08.552 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:08.558 alo = 108, ahi = 1101
2025-07-02 05:47:08.566 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:08.579 blo = 108, bhi = 1101
2025-07-02 05:47:08.590
2025-07-02 05:47:08.599 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:08.605 r"""
2025-07-02 05:47:08.617 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:08.630 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:08.638 synch point, and intraline difference marking is done on the
2025-07-02 05:47:08.646 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:08.652
2025-07-02 05:47:08.659 Example:
2025-07-02 05:47:08.672
2025-07-02 05:47:08.684 >>> d = Differ()
2025-07-02 05:47:08.692 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:08.703 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:08.713 >>> print(''.join(results), end="")
2025-07-02 05:47:08.725 - abcDefghiJkl
2025-07-02 05:47:08.747 + abcdefGhijkl
2025-07-02 05:47:08.764 """
2025-07-02 05:47:08.772
2025-07-02 05:47:08.780 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:08.790 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:08.802 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:08.811 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:08.819 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:08.825
2025-07-02 05:47:08.831 # search for the pair that matches best without being identical
2025-07-02 05:47:08.845 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:08.858 # on junk -- unless we have to)
2025-07-02 05:47:08.867 for j in range(blo, bhi):
2025-07-02 05:47:08.874 bj = b[j]
2025-07-02 05:47:08.880 cruncher.set_seq2(bj)
2025-07-02 05:47:08.890 for i in range(alo, ahi):
2025-07-02 05:47:08.903 ai = a[i]
2025-07-02 05:47:08.914 if ai == bj:
2025-07-02 05:47:08.922 if eqi is None:
2025-07-02 05:47:08.929 eqi, eqj = i, j
2025-07-02 05:47:08.935 continue
2025-07-02 05:47:08.941 cruncher.set_seq1(ai)
2025-07-02 05:47:08.947 # computing similarity is expensive, so use the quick
2025-07-02 05:47:08.958 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:08.968 # compares by a factor of 3.
2025-07-02 05:47:08.980 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:08.991 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:09.000 # of the computation is cached by cruncher
2025-07-02 05:47:09.009 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:09.016 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:09.027 cruncher.ratio() > best_ratio:
2025-07-02 05:47:09.040 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:09.049 if best_ratio < cutoff:
2025-07-02 05:47:09.056 # no non-identical "pretty close" pair
2025-07-02 05:47:09.062 if eqi is None:
2025-07-02 05:47:09.068 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:09.075 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:09.081 return
2025-07-02 05:47:09.089 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:09.098 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:09.105 else:
2025-07-02 05:47:09.112 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:09.119 eqi = None
2025-07-02 05:47:09.127
2025-07-02 05:47:09.136 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:09.145 # identical
2025-07-02 05:47:09.152
2025-07-02 05:47:09.163 # pump out diffs from before the synch point
2025-07-02 05:47:09.174 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:09.184
2025-07-02 05:47:09.191 # do intraline marking on the synch pair
2025-07-02 05:47:09.198 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:09.203 if eqi is None:
2025-07-02 05:47:09.209 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:09.215 atags = btags = ""
2025-07-02 05:47:09.223 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:09.235 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:09.245 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:09.253 if tag == 'replace':
2025-07-02 05:47:09.263 atags += '^' * la
2025-07-02 05:47:09.273 btags += '^' * lb
2025-07-02 05:47:09.280 elif tag == 'delete':
2025-07-02 05:47:09.287 atags += '-' * la
2025-07-02 05:47:09.293 elif tag == 'insert':
2025-07-02 05:47:09.299 btags += '+' * lb
2025-07-02 05:47:09.304 elif tag == 'equal':
2025-07-02 05:47:09.313 atags += ' ' * la
2025-07-02 05:47:09.323 btags += ' ' * lb
2025-07-02 05:47:09.330 else:
2025-07-02 05:47:09.338 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:09.345 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:09.351 else:
2025-07-02 05:47:09.359 # the synch pair is identical
2025-07-02 05:47:09.370 yield '  ' + aelt
2025-07-02 05:47:09.381
2025-07-02 05:47:09.392 # pump out diffs from after the synch point
2025-07-02 05:47:09.403 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:09.414
2025-07-02 05:47:09.427 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:09.442 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:09.454
2025-07-02 05:47:09.465 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:09.474 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:09.484 alo = 109, ahi = 1101
2025-07-02 05:47:09.494 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:09.501 blo = 109, bhi = 1101
2025-07-02 05:47:09.508
2025-07-02 05:47:09.517 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:09.530 g = []
2025-07-02 05:47:09.539 if alo < ahi:
2025-07-02 05:47:09.549 if blo < bhi:
2025-07-02 05:47:09.562 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:09.576 else:
2025-07-02 05:47:09.587 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:09.598 elif blo < bhi:
2025-07-02 05:47:09.610 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:09.619
2025-07-02 05:47:09.626 >       yield from g
2025-07-02 05:47:09.637
2025-07-02 05:47:09.647 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:09.659 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:09.670
2025-07-02 05:47:09.682 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:09.693 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:09.701 alo = 109, ahi = 1101
2025-07-02 05:47:09.711 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:09.724 blo = 109, bhi = 1101
2025-07-02 05:47:09.733
2025-07-02 05:47:09.746 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:09.756 r"""
2025-07-02 05:47:09.763 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:09.770 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:09.779 synch point, and intraline difference marking is done on the
2025-07-02 05:47:09.790 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:09.799
2025-07-02 05:47:09.807 Example:
2025-07-02 05:47:09.814
2025-07-02 05:47:09.821 >>> d = Differ()
2025-07-02 05:47:09.828 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:09.834 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:09.840 >>> print(''.join(results), end="")
2025-07-02 05:47:09.847 - abcDefghiJkl
2025-07-02 05:47:09.859 + abcdefGhijkl
2025-07-02 05:47:09.872 """
2025-07-02 05:47:09.879
2025-07-02 05:47:09.886 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:09.895 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:09.902 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:09.911 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:09.922 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:09.929
2025-07-02 05:47:09.935 # search for the pair that matches best without being identical
2025-07-02 05:47:09.940 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:09.950 # on junk -- unless we have to)
2025-07-02 05:47:09.960 for j in range(blo, bhi):
2025-07-02 05:47:09.969 bj = b[j]
2025-07-02 05:47:09.978 cruncher.set_seq2(bj)
2025-07-02 05:47:09.986 for i in range(alo, ahi):
2025-07-02 05:47:09.995 ai = a[i]
2025-07-02 05:47:10.001 if ai == bj:
2025-07-02 05:47:10.007 if eqi is None:
2025-07-02 05:47:10.013 eqi, eqj = i, j
2025-07-02 05:47:10.018 continue
2025-07-02 05:47:10.030 cruncher.set_seq1(ai)
2025-07-02 05:47:10.046 # computing similarity is expensive, so use the quick
2025-07-02 05:47:10.057 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:10.068 # compares by a factor of 3.
2025-07-02 05:47:10.079 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:10.090 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:10.098 # of the computation is cached by cruncher
2025-07-02 05:47:10.106 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:10.115 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:10.127 cruncher.ratio() > best_ratio:
2025-07-02 05:47:10.138 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:10.145 if best_ratio < cutoff:
2025-07-02 05:47:10.151 # no non-identical "pretty close" pair
2025-07-02 05:47:10.158 if eqi is None:
2025-07-02 05:47:10.169 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:10.182 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:10.194 return
2025-07-02 05:47:10.206 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:10.218 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:10.228 else:
2025-07-02 05:47:10.237 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:10.246 eqi = None
2025-07-02 05:47:10.258
2025-07-02 05:47:10.268 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:10.276 # identical
2025-07-02 05:47:10.283
2025-07-02 05:47:10.291 # pump out diffs from before the synch point
2025-07-02 05:47:10.299 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:10.308
2025-07-02 05:47:10.316 # do intraline marking on the synch pair
2025-07-02 05:47:10.324 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:10.331 if eqi is None:
2025-07-02 05:47:10.338 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:10.346 atags = btags = ""
2025-07-02 05:47:10.354 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:10.363 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:10.370 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:10.377 if tag == 'replace':
2025-07-02 05:47:10.386 atags += '^' * la
2025-07-02 05:47:10.394 btags += '^' * lb
2025-07-02 05:47:10.402 elif tag == 'delete':
2025-07-02 05:47:10.408 atags += '-' * la
2025-07-02 05:47:10.421 elif tag == 'insert':
2025-07-02 05:47:10.431 btags += '+' * lb
2025-07-02 05:47:10.439 elif tag == 'equal':
2025-07-02 05:47:10.446 atags += ' ' * la
2025-07-02 05:47:10.455 btags += ' ' * lb
2025-07-02 05:47:10.465 else:
2025-07-02 05:47:10.475 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:10.486 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:10.494 else:
2025-07-02 05:47:10.508 # the synch pair is identical
2025-07-02 05:47:10.522 yield '  ' + aelt
2025-07-02 05:47:10.532
2025-07-02 05:47:10.540 # pump out diffs from after the synch point
2025-07-02 05:47:10.553 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:10.563
2025-07-02 05:47:10.575 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:10.585 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:10.593
2025-07-02 05:47:10.601 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:10.610 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:10.616 alo = 110, ahi = 1101
2025-07-02 05:47:10.630 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:10.640 blo = 110, bhi = 1101
2025-07-02 05:47:10.648
2025-07-02 05:47:10.655 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:10.663 g = []
2025-07-02 05:47:10.675 if alo < ahi:
2025-07-02 05:47:10.688 if blo < bhi:
2025-07-02 05:47:10.696 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:10.707 else:
2025-07-02 05:47:10.716 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:10.723 elif blo < bhi:
2025-07-02 05:47:10.731 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:10.739
2025-07-02 05:47:10.749 >       yield from g
2025-07-02 05:47:10.764
2025-07-02 05:47:10.780 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:10.789 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:10.798
2025-07-02 05:47:10.803 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:10.810 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:10.816 alo = 110, ahi = 1101
2025-07-02 05:47:10.824 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:10.831 blo = 110, bhi = 1101
2025-07-02 05:47:10.839
2025-07-02 05:47:10.848 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:10.854 r"""
2025-07-02 05:47:10.861 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:10.868 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:10.875 synch point, and intraline difference marking is done on the
2025-07-02 05:47:10.884 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:10.895
2025-07-02 05:47:10.903 Example:
2025-07-02 05:47:10.913
2025-07-02 05:47:10.926 >>> d = Differ()
2025-07-02 05:47:10.937 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:10.947 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:10.954 >>> print(''.join(results), end="")
2025-07-02 05:47:10.963 - abcDefghiJkl
2025-07-02 05:47:10.978 + abcdefGhijkl
2025-07-02 05:47:10.998 """
2025-07-02 05:47:11.008
2025-07-02 05:47:11.018 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:11.028 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:11.036 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:11.043 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:11.051 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:11.058
2025-07-02 05:47:11.071 # search for the pair that matches best without being identical
2025-07-02 05:47:11.080 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:11.087 # on junk -- unless we have to)
2025-07-02 05:47:11.093 for j in range(blo, bhi):
2025-07-02 05:47:11.099 bj = b[j]
2025-07-02 05:47:11.107 cruncher.set_seq2(bj)
2025-07-02 05:47:11.118 for i in range(alo, ahi):
2025-07-02 05:47:11.127 ai = a[i]
2025-07-02 05:47:11.135 if ai == bj:
2025-07-02 05:47:11.145 if eqi is None:
2025-07-02 05:47:11.158 eqi, eqj = i, j
2025-07-02 05:47:11.172 continue
2025-07-02 05:47:11.183 cruncher.set_seq1(ai)
2025-07-02 05:47:11.192 # computing similarity is expensive, so use the quick
2025-07-02 05:47:11.199 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:11.207 # compares by a factor of 3.
2025-07-02 05:47:11.213 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:11.225 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:11.234 # of the computation is cached by cruncher
2025-07-02 05:47:11.243 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:11.252 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:11.262 cruncher.ratio() > best_ratio:
2025-07-02 05:47:11.272 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:11.286 if best_ratio < cutoff:
2025-07-02 05:47:11.298 # no non-identical "pretty close" pair
2025-07-02 05:47:11.305 if eqi is None:
2025-07-02 05:47:11.311 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:11.321 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:11.330 return
2025-07-02 05:47:11.344 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:11.355 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:11.365 else:
2025-07-02 05:47:11.378 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:11.389 eqi = None
2025-07-02 05:47:11.397
2025-07-02 05:47:11.408 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:11.420 # identical
2025-07-02 05:47:11.428
2025-07-02 05:47:11.435 # pump out diffs from before the synch point
2025-07-02 05:47:11.441 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:11.453
2025-07-02 05:47:11.461 # do intraline marking on the synch pair
2025-07-02 05:47:11.470 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:11.477 if eqi is None:
2025-07-02 05:47:11.484 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:11.489 atags = btags = ""
2025-07-02 05:47:11.494 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:11.499 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:11.504 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:11.508 if tag == 'replace':
2025-07-02 05:47:11.515 atags += '^' * la
2025-07-02 05:47:11.528 btags += '^' * lb
2025-07-02 05:47:11.536 elif tag == 'delete':
2025-07-02 05:47:11.543 atags += '-' * la
2025-07-02 05:47:11.550 elif tag == 'insert':
2025-07-02 05:47:11.557 btags += '+' * lb
2025-07-02 05:47:11.564 elif tag == 'equal':
2025-07-02 05:47:11.571 atags += ' ' * la
2025-07-02 05:47:11.577 btags += ' ' * lb
2025-07-02 05:47:11.583 else:
2025-07-02 05:47:11.590 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:11.603 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:11.615 else:
2025-07-02 05:47:11.625 # the synch pair is identical
2025-07-02 05:47:11.633 yield '  ' + aelt
2025-07-02 05:47:11.639
2025-07-02 05:47:11.653 # pump out diffs from after the synch point
2025-07-02 05:47:11.665 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:11.678
2025-07-02 05:47:11.687 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:11.694 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:11.701
2025-07-02 05:47:11.707 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:11.714 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:11.726 alo = 111, ahi = 1101
2025-07-02 05:47:11.736 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:11.744 blo = 111, bhi = 1101
2025-07-02 05:47:11.751
2025-07-02 05:47:11.759 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:11.770 g = []
2025-07-02 05:47:11.778 if alo < ahi:
2025-07-02 05:47:11.785 if blo < bhi:
2025-07-02 05:47:11.795 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:11.803 else:
2025-07-02 05:47:11.811 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:11.818 elif blo < bhi:
2025-07-02 05:47:11.828 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:11.838
2025-07-02 05:47:11.846 >       yield from g
2025-07-02 05:47:11.855
2025-07-02 05:47:11.863 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:11.871 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:11.881
2025-07-02 05:47:11.893 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:11.903 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:11.915 alo = 111, ahi = 1101
2025-07-02 05:47:11.929 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:11.941 blo = 111, bhi = 1101
2025-07-02 05:47:11.950
2025-07-02 05:47:11.961 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:11.971 r"""
2025-07-02 05:47:11.980 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:11.988 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:11.995 synch point, and intraline difference marking is done on the
2025-07-02 05:47:12.004 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:12.015
2025-07-02 05:47:12.023 Example:
2025-07-02 05:47:12.031
2025-07-02 05:47:12.038 >>> d = Differ()
2025-07-02 05:47:12.044 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:12.050 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:12.055 >>> print(''.join(results), end="")
2025-07-02 05:47:12.061 - abcDefghiJkl
2025-07-02 05:47:12.077 + abcdefGhijkl
2025-07-02 05:47:12.093 """
2025-07-02 05:47:12.102
2025-07-02 05:47:12.108 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:12.114 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:12.119 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:12.127 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:12.138 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:12.146
2025-07-02 05:47:12.159 # search for the pair that matches best without being identical
2025-07-02 05:47:12.169 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:12.176 # on junk -- unless we have to)
2025-07-02 05:47:12.183 for j in range(blo, bhi):
2025-07-02 05:47:12.190 bj = b[j]
2025-07-02 05:47:12.196 cruncher.set_seq2(bj)
2025-07-02 05:47:12.202 for i in range(alo, ahi):
2025-07-02 05:47:12.211 ai = a[i]
2025-07-02 05:47:12.222 if ai == bj:
2025-07-02 05:47:12.235 if eqi is None:
2025-07-02 05:47:12.246 eqi, eqj = i, j
2025-07-02 05:47:12.255 continue
2025-07-02 05:47:12.264 cruncher.set_seq1(ai)
2025-07-02 05:47:12.274 # computing similarity is expensive, so use the quick
2025-07-02 05:47:12.284 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:12.295 # compares by a factor of 3.
2025-07-02 05:47:12.303 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:12.311 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:12.319 # of the computation is cached by cruncher
2025-07-02 05:47:12.332 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:12.345 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:12.353 cruncher.ratio() > best_ratio:
2025-07-02 05:47:12.365 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:12.375 if best_ratio < cutoff:
2025-07-02 05:47:12.384 # no non-identical "pretty close" pair
2025-07-02 05:47:12.395 if eqi is None:
2025-07-02 05:47:12.404 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:12.413 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:12.420 return
2025-07-02 05:47:12.426 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:12.432 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:12.439 else:
2025-07-02 05:47:12.450 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:12.458 eqi = None
2025-07-02 05:47:12.470
2025-07-02 05:47:12.481 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:12.488 # identical
2025-07-02 05:47:12.494
2025-07-02 05:47:12.505 # pump out diffs from before the synch point
2025-07-02 05:47:12.516 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:12.523
2025-07-02 05:47:12.531 # do intraline marking on the synch pair
2025-07-02 05:47:12.541 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:12.551 if eqi is None:
2025-07-02 05:47:12.559 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:12.566 atags = btags = ""
2025-07-02 05:47:12.577 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:12.589 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:12.605 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:12.615 if tag == 'replace':
2025-07-02 05:47:12.624 atags += '^' * la
2025-07-02 05:47:12.637 btags += '^' * lb
2025-07-02 05:47:12.648 elif tag == 'delete':
2025-07-02 05:47:12.657 atags += '-' * la
2025-07-02 05:47:12.668 elif tag == 'insert':
2025-07-02 05:47:12.678 btags += '+' * lb
2025-07-02 05:47:12.687 elif tag == 'equal':
2025-07-02 05:47:12.695 atags += ' ' * la
2025-07-02 05:47:12.702 btags += ' ' * lb
2025-07-02 05:47:12.708 else:
2025-07-02 05:47:12.714 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:12.720 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:12.729 else:
2025-07-02 05:47:12.741 # the synch pair is identical
2025-07-02 05:47:12.751 yield '  ' + aelt
2025-07-02 05:47:12.758
2025-07-02 05:47:12.766 # pump out diffs from after the synch point
2025-07-02 05:47:12.774 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:12.786
2025-07-02 05:47:12.795 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:12.802 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:12.813
2025-07-02 05:47:12.824 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:12.835 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:12.846 alo = 114, ahi = 1101
2025-07-02 05:47:12.855 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:12.863 blo = 114, bhi = 1101
2025-07-02 05:47:12.870
2025-07-02 05:47:12.878 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:12.884 g = []
2025-07-02 05:47:12.891 if alo < ahi:
2025-07-02 05:47:12.897 if blo < bhi:
2025-07-02 05:47:12.903 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:12.909 else:
2025-07-02 05:47:12.915 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:12.920 elif blo < bhi:
2025-07-02 05:47:12.926 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:12.932
2025-07-02 05:47:12.938 >       yield from g
2025-07-02 05:47:12.943
2025-07-02 05:47:12.952 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:12.965 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:12.978
2025-07-02 05:47:12.989 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:13.002 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:13.014 alo = 114, ahi = 1101
2025-07-02 05:47:13.031 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:13.042 blo = 114, bhi = 1101
2025-07-02 05:47:13.056
2025-07-02 05:47:13.067 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:13.076 r"""
2025-07-02 05:47:13.084 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:13.092 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:13.099 synch point, and intraline difference marking is done on the
2025-07-02 05:47:13.108 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:13.121
2025-07-02 05:47:13.131 Example:
2025-07-02 05:47:13.141
2025-07-02 05:47:13.151 >>> d = Differ()
2025-07-02 05:47:13.161 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:13.168 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:13.174 >>> print(''.join(results), end="")
2025-07-02 05:47:13.187 - abcDefghiJkl
2025-07-02 05:47:13.203 + abcdefGhijkl
2025-07-02 05:47:13.217 """
2025-07-02 05:47:13.222
2025-07-02 05:47:13.229 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:13.235 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:13.241 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:13.247 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:13.255 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:13.262
2025-07-02 05:47:13.270 # search for the pair that matches best without being identical
2025-07-02 05:47:13.279 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:13.291 # on junk -- unless we have to)
2025-07-02 05:47:13.300 for j in range(blo, bhi):
2025-07-02 05:47:13.307 bj = b[j]
2025-07-02 05:47:13.314 cruncher.set_seq2(bj)
2025-07-02 05:47:13.320 for i in range(alo, ahi):
2025-07-02 05:47:13.326 ai = a[i]
2025-07-02 05:47:13.332 if ai == bj:
2025-07-02 05:47:13.339 if eqi is None:
2025-07-02 05:47:13.345 eqi, eqj = i, j
2025-07-02 05:47:13.349 continue
2025-07-02 05:47:13.354 cruncher.set_seq1(ai)
2025-07-02 05:47:13.359 # computing similarity is expensive, so use the quick
2025-07-02 05:47:13.367 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:13.379 # compares by a factor of 3.
2025-07-02 05:47:13.389 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:13.398 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:13.405 # of the computation is cached by cruncher
2025-07-02 05:47:13.411 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:13.416 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:13.421 cruncher.ratio() > best_ratio:
2025-07-02 05:47:13.426 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:13.431 if best_ratio < cutoff:
2025-07-02 05:47:13.436 # no non-identical "pretty close" pair
2025-07-02 05:47:13.441 if eqi is None:
2025-07-02 05:47:13.446 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:13.451 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:13.456 return
2025-07-02 05:47:13.460 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:13.465 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:13.470 else:
2025-07-02 05:47:13.475 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:13.480 eqi = None
2025-07-02 05:47:13.485
2025-07-02 05:47:13.490 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:13.495 # identical
2025-07-02 05:47:13.499
2025-07-02 05:47:13.504 # pump out diffs from before the synch point
2025-07-02 05:47:13.510 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:13.516
2025-07-02 05:47:13.521 # do intraline marking on the synch pair
2025-07-02 05:47:13.526 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:13.540 if eqi is None:
2025-07-02 05:47:13.548 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:13.554 atags = btags = ""
2025-07-02 05:47:13.559 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:13.572 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:13.586 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:13.593 if tag == 'replace':
2025-07-02 05:47:13.601 atags += '^' * la
2025-07-02 05:47:13.609 btags += '^' * lb
2025-07-02 05:47:13.614 elif tag == 'delete':
2025-07-02 05:47:13.619 atags += '-' * la
2025-07-02 05:47:13.624 elif tag == 'insert':
2025-07-02 05:47:13.630 btags += '+' * lb
2025-07-02 05:47:13.636 elif tag == 'equal':
2025-07-02 05:47:13.644 atags += ' ' * la
2025-07-02 05:47:13.652 btags += ' ' * lb
2025-07-02 05:47:13.659 else:
2025-07-02 05:47:13.669 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:13.680 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:13.689 else:
2025-07-02 05:47:13.695 # the synch pair is identical
2025-07-02 05:47:13.702 yield '  ' + aelt
2025-07-02 05:47:13.708
2025-07-02 05:47:13.714 # pump out diffs from after the synch point
2025-07-02 05:47:13.729 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:13.739
2025-07-02 05:47:13.749 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:13.762 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:13.775
2025-07-02 05:47:13.783 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:13.790 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:13.796 alo = 115, ahi = 1101
2025-07-02 05:47:13.803 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:13.810 blo = 115, bhi = 1101
2025-07-02 05:47:13.822
2025-07-02 05:47:13.836 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:13.847 g = []
2025-07-02 05:47:13.856 if alo < ahi:
2025-07-02 05:47:13.864 if blo < bhi:
2025-07-02 05:47:13.877 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:13.889 else:
2025-07-02 05:47:13.902 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:13.911 elif blo < bhi:
2025-07-02 05:47:13.920 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:13.927
2025-07-02 05:47:13.933 >       yield from g
2025-07-02 05:47:13.939
2025-07-02 05:47:13.945 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:13.952 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:13.957
2025-07-02 05:47:13.963 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:13.972 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:13.978 alo = 115, ahi = 1101
2025-07-02 05:47:13.987 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:13.994 blo = 115, bhi = 1101
2025-07-02 05:47:14.003
2025-07-02 05:47:14.014 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:14.022 r"""
2025-07-02 05:47:14.030 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:14.035 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:14.040 synch point, and intraline difference marking is done on the
2025-07-02 05:47:14.046 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:14.052
2025-07-02 05:47:14.059 Example:
2025-07-02 05:47:14.065
2025-07-02 05:47:14.071 >>> d = Differ()
2025-07-02 05:47:14.079 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:14.092 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:14.101 >>> print(''.join(results), end="")
2025-07-02 05:47:14.114 - abcDefghiJkl
2025-07-02 05:47:14.139 + abcdefGhijkl
2025-07-02 05:47:14.156 """
2025-07-02 05:47:14.163
2025-07-02 05:47:14.169 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:14.175 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:14.188 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:14.201 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:14.213 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:14.223
2025-07-02 05:47:14.232 # search for the pair that matches best without being identical
2025-07-02 05:47:14.240 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:14.247 # on junk -- unless we have to)
2025-07-02 05:47:14.257 for j in range(blo, bhi):
2025-07-02 05:47:14.270 bj = b[j]
2025-07-02 05:47:14.280 cruncher.set_seq2(bj)
2025-07-02 05:47:14.293 for i in range(alo, ahi):
2025-07-02 05:47:14.301 ai = a[i]
2025-07-02 05:47:14.308 if ai == bj:
2025-07-02 05:47:14.318 if eqi is None:
2025-07-02 05:47:14.329 eqi, eqj = i, j
2025-07-02 05:47:14.343 continue
2025-07-02 05:47:14.352 cruncher.set_seq1(ai)
2025-07-02 05:47:14.360 # computing similarity is expensive, so use the quick
2025-07-02 05:47:14.368 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:14.374 # compares by a factor of 3.
2025-07-02 05:47:14.382 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:14.394 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:14.403 # of the computation is cached by cruncher
2025-07-02 05:47:14.412 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:14.419 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:14.425 cruncher.ratio() > best_ratio:
2025-07-02 05:47:14.430 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:14.443 if best_ratio < cutoff:
2025-07-02 05:47:14.454 # no non-identical "pretty close" pair
2025-07-02 05:47:14.463 if eqi is None:
2025-07-02 05:47:14.470 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:14.481 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:14.493 return
2025-07-02 05:47:14.507 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:14.517 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:14.524 else:
2025-07-02 05:47:14.533 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:14.540 eqi = None
2025-07-02 05:47:14.546
2025-07-02 05:47:14.556 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:14.569 # identical
2025-07-02 05:47:14.582
2025-07-02 05:47:14.593 # pump out diffs from before the synch point
2025-07-02 05:47:14.604 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:14.612
2025-07-02 05:47:14.620 # do intraline marking on the synch pair
2025-07-02 05:47:14.626 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:14.637 if eqi is None:
2025-07-02 05:47:14.647 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:14.657 atags = btags = ""
2025-07-02 05:47:14.665 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:14.672 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:14.680 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:14.686 if tag == 'replace':
2025-07-02 05:47:14.698 atags += '^' * la
2025-07-02 05:47:14.707 btags += '^' * lb
2025-07-02 05:47:14.715 elif tag == 'delete':
2025-07-02 05:47:14.721 atags += '-' * la
2025-07-02 05:47:14.727 elif tag == 'insert':
2025-07-02 05:47:14.735 btags += '+' * lb
2025-07-02 05:47:14.746 elif tag == 'equal':
2025-07-02 05:47:14.759 atags += ' ' * la
2025-07-02 05:47:14.767 btags += ' ' * lb
2025-07-02 05:47:14.775 else:
2025-07-02 05:47:14.782 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:14.789 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:14.796 else:
2025-07-02 05:47:14.802 # the synch pair is identical
2025-07-02 05:47:14.814 yield '  ' + aelt
2025-07-02 05:47:14.824
2025-07-02 05:47:14.832 # pump out diffs from after the synch point
2025-07-02 05:47:14.839 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:14.848
2025-07-02 05:47:14.857 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:14.865 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:14.871
2025-07-02 05:47:14.877 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:14.883 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:14.888 alo = 116, ahi = 1101
2025-07-02 05:47:14.895 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:14.900 blo = 116, bhi = 1101
2025-07-02 05:47:14.906
2025-07-02 05:47:14.912 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:14.919 g = []
2025-07-02 05:47:14.926 if alo < ahi:
2025-07-02 05:47:14.936 if blo < bhi:
2025-07-02 05:47:14.945 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:14.953 else:
2025-07-02 05:47:14.960 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:14.965 elif blo < bhi:
2025-07-02 05:47:14.971 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:14.977
2025-07-02 05:47:14.982 >       yield from g
2025-07-02 05:47:14.988
2025-07-02 05:47:14.994 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:15.001 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:15.007
2025-07-02 05:47:15.014 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:15.020 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:15.025 alo = 116, ahi = 1101
2025-07-02 05:47:15.031 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:15.037 blo = 116, bhi = 1101
2025-07-02 05:47:15.043
2025-07-02 05:47:15.049 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:15.056 r"""
2025-07-02 05:47:15.062 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:15.068 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:15.074 synch point, and intraline difference marking is done on the
2025-07-02 05:47:15.083 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:15.096
2025-07-02 05:47:15.106 Example:
2025-07-02 05:47:15.118
2025-07-02 05:47:15.128 >>> d = Differ()
2025-07-02 05:47:15.134 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:15.141 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:15.147 >>> print(''.join(results), end="")
2025-07-02 05:47:15.153 - abcDefghiJkl
2025-07-02 05:47:15.165 + abcdefGhijkl
2025-07-02 05:47:15.177 """
2025-07-02 05:47:15.184
2025-07-02 05:47:15.192 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:15.200 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:15.207 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:15.215 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:15.223 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:15.231
2025-07-02 05:47:15.245 # search for the pair that matches best without being identical
2025-07-02 05:47:15.258 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:15.266 # on junk -- unless we have to)
2025-07-02 05:47:15.274 for j in range(blo, bhi):
2025-07-02 05:47:15.283 bj = b[j]
2025-07-02 05:47:15.295 cruncher.set_seq2(bj)
2025-07-02 05:47:15.304 for i in range(alo, ahi):
2025-07-02 05:47:15.312 ai = a[i]
2025-07-02 05:47:15.318 if ai == bj:
2025-07-02 05:47:15.323 if eqi is None:
2025-07-02 05:47:15.329 eqi, eqj = i, j
2025-07-02 05:47:15.335 continue
2025-07-02 05:47:15.343 cruncher.set_seq1(ai)
2025-07-02 05:47:15.350 # computing similarity is expensive, so use the quick
2025-07-02 05:47:15.358 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:15.370 # compares by a factor of 3.
2025-07-02 05:47:15.383 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:15.393 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:15.401 # of the computation is cached by cruncher
2025-07-02 05:47:15.408 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:15.414 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:15.424 cruncher.ratio() > best_ratio:
2025-07-02 05:47:15.438 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:15.450 if best_ratio < cutoff:
2025-07-02 05:47:15.460 # no non-identical "pretty close" pair
2025-07-02 05:47:15.471 if eqi is None:
2025-07-02 05:47:15.479 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:15.485 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:15.490 return
2025-07-02 05:47:15.495 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:15.500 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:15.504 else:
2025-07-02 05:47:15.510 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:15.516 eqi = None
2025-07-02 05:47:15.521
2025-07-02 05:47:15.531 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:15.540 # identical
2025-07-02 05:47:15.547
2025-07-02 05:47:15.554 # pump out diffs from before the synch point
2025-07-02 05:47:15.565 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:15.575
2025-07-02 05:47:15.583 # do intraline marking on the synch pair
2025-07-02 05:47:15.590 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:15.596 if eqi is None:
2025-07-02 05:47:15.603 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:15.610 atags = btags = ""
2025-07-02 05:47:15.621 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:15.631 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:15.638 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:15.644 if tag == 'replace':
2025-07-02 05:47:15.651 atags += '^' * la
2025-07-02 05:47:15.658 btags += '^' * lb
2025-07-02 05:47:15.669 elif tag == 'delete':
2025-07-02 05:47:15.678 atags += '-' * la
2025-07-02 05:47:15.686 elif tag == 'insert':
2025-07-02 05:47:15.692 btags += '+' * lb
2025-07-02 05:47:15.699 elif tag == 'equal':
2025-07-02 05:47:15.707 atags += ' ' * la
2025-07-02 05:47:15.718 btags += ' ' * lb
2025-07-02 05:47:15.727 else:
2025-07-02 05:47:15.734 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:15.740 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:15.748 else:
2025-07-02 05:47:15.755 # the synch pair is identical
2025-07-02 05:47:15.763 yield '  ' + aelt
2025-07-02 05:47:15.770
2025-07-02 05:47:15.781 # pump out diffs from after the synch point
2025-07-02 05:47:15.793 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:15.800
2025-07-02 05:47:15.807 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:15.818 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:15.828
2025-07-02 05:47:15.837 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:15.849 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:15.863 alo = 117, ahi = 1101
2025-07-02 05:47:15.873 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:15.881 blo = 117, bhi = 1101
2025-07-02 05:47:15.887
2025-07-02 05:47:15.895 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:15.905 g = []
2025-07-02 05:47:15.914 if alo < ahi:
2025-07-02 05:47:15.921 if blo < bhi:
2025-07-02 05:47:15.929 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:15.935 else:
2025-07-02 05:47:15.943 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:15.955 elif blo < bhi:
2025-07-02 05:47:15.962 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:15.971
2025-07-02 05:47:15.978 >       yield from g
2025-07-02 05:47:15.989
2025-07-02 05:47:15.998 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:16.006 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:16.013
2025-07-02 05:47:16.020 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:16.026 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:16.032 alo = 117, ahi = 1101
2025-07-02 05:47:16.037 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:16.042 blo = 117, bhi = 1101
2025-07-02 05:47:16.047
2025-07-02 05:47:16.052 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:16.059 r"""
2025-07-02 05:47:16.066 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:16.075 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:16.086 synch point, and intraline difference marking is done on the
2025-07-02 05:47:16.096 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:16.103
2025-07-02 05:47:16.110 Example:
2025-07-02 05:47:16.117
2025-07-02 05:47:16.123 >>> d = Differ()
2025-07-02 05:47:16.128 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:16.134 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:16.145 >>> print(''.join(results), end="")
2025-07-02 05:47:16.154 - abcDefghiJkl
2025-07-02 05:47:16.169 + abcdefGhijkl
2025-07-02 05:47:16.190 """
2025-07-02 05:47:16.199
2025-07-02 05:47:16.211 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:16.220 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:16.227 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:16.240 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:16.252 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:16.264
2025-07-02 05:47:16.275 # search for the pair that matches best without being identical
2025-07-02 05:47:16.283 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:16.291 # on junk -- unless we have to)
2025-07-02 05:47:16.297 for j in range(blo, bhi):
2025-07-02 05:47:16.302 bj = b[j]
2025-07-02 05:47:16.307 cruncher.set_seq2(bj)
2025-07-02 05:47:16.311 for i in range(alo, ahi):
2025-07-02 05:47:16.317 ai = a[i]
2025-07-02 05:47:16.322 if ai == bj:
2025-07-02 05:47:16.327 if eqi is None:
2025-07-02 05:47:16.332 eqi, eqj = i, j
2025-07-02 05:47:16.337 continue
2025-07-02 05:47:16.342 cruncher.set_seq1(ai)
2025-07-02 05:47:16.347 # computing similarity is expensive, so use the quick
2025-07-02 05:47:16.354 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:16.365 # compares by a factor of 3.
2025-07-02 05:47:16.377 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:16.384 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:16.390 # of the computation is cached by cruncher
2025-07-02 05:47:16.398 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:16.412 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:16.421 cruncher.ratio() > best_ratio:
2025-07-02 05:47:16.429 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:16.437 if best_ratio < cutoff:
2025-07-02 05:47:16.444 # no non-identical "pretty close" pair
2025-07-02 05:47:16.452 if eqi is None:
2025-07-02 05:47:16.459 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:16.465 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:16.471 return
2025-07-02 05:47:16.477 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:16.483 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:16.489 else:
2025-07-02 05:47:16.495 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:16.501 eqi = None
2025-07-02 05:47:16.506
2025-07-02 05:47:16.512 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:16.519 # identical
2025-07-02 05:47:16.525
2025-07-02 05:47:16.532 # pump out diffs from before the synch point
2025-07-02 05:47:16.540 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:16.547
2025-07-02 05:47:16.554 # do intraline marking on the synch pair
2025-07-02 05:47:16.566 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:16.577 if eqi is None:
2025-07-02 05:47:16.585 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:16.591 atags = btags = ""
2025-07-02 05:47:16.597 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:16.603 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:16.609 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:16.615 if tag == 'replace':
2025-07-02 05:47:16.622 atags += '^' * la
2025-07-02 05:47:16.630 btags += '^' * lb
2025-07-02 05:47:16.642 elif tag == 'delete':
2025-07-02 05:47:16.650 atags += '-' * la
2025-07-02 05:47:16.657 elif tag == 'insert':
2025-07-02 05:47:16.670 btags += '+' * lb
2025-07-02 05:47:16.679 elif tag == 'equal':
2025-07-02 05:47:16.687 atags += ' ' * la
2025-07-02 05:47:16.694 btags += ' ' * lb
2025-07-02 05:47:16.705 else:
2025-07-02 05:47:16.715 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:16.722 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:16.729 else:
2025-07-02 05:47:16.736 # the synch pair is identical
2025-07-02 05:47:16.743 yield '  ' + aelt
2025-07-02 05:47:16.751
2025-07-02 05:47:16.762 # pump out diffs from after the synch point
2025-07-02 05:47:16.770 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:16.776
2025-07-02 05:47:16.783 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:16.791 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:16.803
2025-07-02 05:47:16.811 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:16.820 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:16.826 alo = 118, ahi = 1101
2025-07-02 05:47:16.838 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:16.850 blo = 118, bhi = 1101
2025-07-02 05:47:16.859
2025-07-02 05:47:16.866 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:16.873 g = []
2025-07-02 05:47:16.879 if alo < ahi:
2025-07-02 05:47:16.885 if blo < bhi:
2025-07-02 05:47:16.891 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:16.897 else:
2025-07-02 05:47:16.904 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:16.912 elif blo < bhi:
2025-07-02 05:47:16.919 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:16.926
2025-07-02 05:47:16.936 >       yield from g
2025-07-02 05:47:16.949
2025-07-02 05:47:16.962 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:16.972 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:16.980
2025-07-02 05:47:16.991 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:17.002 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:17.009 alo = 118, ahi = 1101
2025-07-02 05:47:17.017 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:17.024 blo = 118, bhi = 1101
2025-07-02 05:47:17.030
2025-07-02 05:47:17.038 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:17.049 r"""
2025-07-02 05:47:17.059 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:17.067 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:17.073 synch point, and intraline difference marking is done on the
2025-07-02 05:47:17.078 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:17.083
2025-07-02 05:47:17.089 Example:
2025-07-02 05:47:17.095
2025-07-02 05:47:17.102 >>> d = Differ()
2025-07-02 05:47:17.108 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:17.116 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:17.123 >>> print(''.join(results), end="")
2025-07-02 05:47:17.131 - abcDefghiJkl
2025-07-02 05:47:17.151 + abcdefGhijkl
2025-07-02 05:47:17.166 """
2025-07-02 05:47:17.171
2025-07-02 05:47:17.176 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:17.182 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:17.187 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:17.194 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:17.200 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:17.207
2025-07-02 05:47:17.214 # search for the pair that matches best without being identical
2025-07-02 05:47:17.223 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:17.233 # on junk -- unless we have to)
2025-07-02 05:47:17.241 for j in range(blo, bhi):
2025-07-02 05:47:17.253 bj = b[j]
2025-07-02 05:47:17.261 cruncher.set_seq2(bj)
2025-07-02 05:47:17.266 for i in range(alo, ahi):
2025-07-02 05:47:17.272 ai = a[i]
2025-07-02 05:47:17.278 if ai == bj:
2025-07-02 05:47:17.284 if eqi is None:
2025-07-02 05:47:17.290 eqi, eqj = i, j
2025-07-02 05:47:17.298 continue
2025-07-02 05:47:17.305 cruncher.set_seq1(ai)
2025-07-02 05:47:17.312 # computing similarity is expensive, so use the quick
2025-07-02 05:47:17.324 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:17.337 # compares by a factor of 3.
2025-07-02 05:47:17.348 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:17.357 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:17.364 # of the computation is cached by cruncher
2025-07-02 05:47:17.376 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:17.386 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:17.393 cruncher.ratio() > best_ratio:
2025-07-02 05:47:17.401 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:17.413 if best_ratio < cutoff:
2025-07-02 05:47:17.419 # no non-identical "pretty close" pair
2025-07-02 05:47:17.426 if eqi is None:
2025-07-02 05:47:17.432 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:17.438 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:17.445 return
2025-07-02 05:47:17.453 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:17.460 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:17.468 else:
2025-07-02 05:47:17.480 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:17.496 eqi = None
2025-07-02 05:47:17.505
2025-07-02 05:47:17.516 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:17.529 # identical
2025-07-02 05:47:17.537
2025-07-02 05:47:17.545 # pump out diffs from before the synch point
2025-07-02 05:47:17.551 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:17.557
2025-07-02 05:47:17.562 # do intraline marking on the synch pair
2025-07-02 05:47:17.567 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:17.571 if eqi is None:
2025-07-02 05:47:17.580 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:17.593 atags = btags = ""
2025-07-02 05:47:17.604 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:17.616 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:17.627 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:17.634 if tag == 'replace':
2025-07-02 05:47:17.641 atags += '^' * la
2025-07-02 05:47:17.652 btags += '^' * lb
2025-07-02 05:47:17.658 elif tag == 'delete':
2025-07-02 05:47:17.666 atags += '-' * la
2025-07-02 05:47:17.674 elif tag == 'insert':
2025-07-02 05:47:17.683 btags += '+' * lb
2025-07-02 05:47:17.695 elif tag == 'equal':
2025-07-02 05:47:17.703 atags += ' ' * la
2025-07-02 05:47:17.714 btags += ' ' * lb
2025-07-02 05:47:17.722 else:
2025-07-02 05:47:17.733 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:17.741 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:17.748 else:
2025-07-02 05:47:17.756 # the synch pair is identical
2025-07-02 05:47:17.763 yield '  ' + aelt
2025-07-02 05:47:17.770
2025-07-02 05:47:17.776 # pump out diffs from after the synch point
2025-07-02 05:47:17.782 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:17.788
2025-07-02 05:47:17.799 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:17.809 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:17.817
2025-07-02 05:47:17.823 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:17.829 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:17.837 alo = 119, ahi = 1101
2025-07-02 05:47:17.846 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:17.855 blo = 119, bhi = 1101
2025-07-02 05:47:17.867
2025-07-02 05:47:17.875 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:17.882 g = []
2025-07-02 05:47:17.888 if alo < ahi:
2025-07-02 05:47:17.895 if blo < bhi:
2025-07-02 05:47:17.902 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:17.913 else:
2025-07-02 05:47:17.922 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:17.929 elif blo < bhi:
2025-07-02 05:47:17.938 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:17.945
2025-07-02 05:47:17.953 >       yield from g
2025-07-02 05:47:17.959
2025-07-02 05:47:17.967 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:17.975 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:17.982
2025-07-02 05:47:17.990 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:17.999 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:18.007 alo = 119, ahi = 1101
2025-07-02 05:47:18.017 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:18.024 blo = 119, bhi = 1101
2025-07-02 05:47:18.032
2025-07-02 05:47:18.040 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:18.048 r"""
2025-07-02 05:47:18.055 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:18.063 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:18.071 synch point, and intraline difference marking is done on the
2025-07-02 05:47:18.079 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:18.087
2025-07-02 05:47:18.094 Example:
2025-07-02 05:47:18.104
2025-07-02 05:47:18.111 >>> d = Differ()
2025-07-02 05:47:18.116 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:18.124 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:18.134 >>> print(''.join(results), end="")
2025-07-02 05:47:18.141 - abcDefghiJkl
2025-07-02 05:47:18.155 + abcdefGhijkl
2025-07-02 05:47:18.172 """
2025-07-02 05:47:18.183
2025-07-02 05:47:18.190 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:18.197 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:18.204 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:18.212 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:18.220 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:18.227
2025-07-02 05:47:18.234 # search for the pair that matches best without being identical
2025-07-02 05:47:18.245 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:18.258 # on junk -- unless we have to)
2025-07-02 05:47:18.266 for j in range(blo, bhi):
2025-07-02 05:47:18.273 bj = b[j]
2025-07-02 05:47:18.283 cruncher.set_seq2(bj)
2025-07-02 05:47:18.295 for i in range(alo, ahi):
2025-07-02 05:47:18.305 ai = a[i]
2025-07-02 05:47:18.313 if ai == bj:
2025-07-02 05:47:18.320 if eqi is None:
2025-07-02 05:47:18.333 eqi, eqj = i, j
2025-07-02 05:47:18.341 continue
2025-07-02 05:47:18.349 cruncher.set_seq1(ai)
2025-07-02 05:47:18.355 # computing similarity is expensive, so use the quick
2025-07-02 05:47:18.366 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:18.373 # compares by a factor of 3.
2025-07-02 05:47:18.381 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:18.388 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:18.396 # of the computation is cached by cruncher
2025-07-02 05:47:18.404 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:18.411 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:18.419 cruncher.ratio() > best_ratio:
2025-07-02 05:47:18.427 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:18.434 if best_ratio < cutoff:
2025-07-02 05:47:18.443 # no non-identical "pretty close" pair
2025-07-02 05:47:18.455 if eqi is None:
2025-07-02 05:47:18.464 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:18.476 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:18.485 return
2025-07-02 05:47:18.493 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:18.498 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:18.509 else:
2025-07-02 05:47:18.515 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:18.521 eqi = None
2025-07-02 05:47:18.528
2025-07-02 05:47:18.535 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:18.546 # identical
2025-07-02 05:47:18.556
2025-07-02 05:47:18.564 # pump out diffs from before the synch point
2025-07-02 05:47:18.573 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:18.579
2025-07-02 05:47:18.586 # do intraline marking on the synch pair
2025-07-02 05:47:18.594 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:18.601 if eqi is None:
2025-07-02 05:47:18.607 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:18.615 atags = btags = ""
2025-07-02 05:47:18.630 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:18.640 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:18.648 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:18.655 if tag == 'replace':
2025-07-02 05:47:18.667 atags += '^' * la
2025-07-02 05:47:18.678 btags += '^' * lb
2025-07-02 05:47:18.686 elif tag == 'delete':
2025-07-02 05:47:18.692 atags += '-' * la
2025-07-02 05:47:18.697 elif tag == 'insert':
2025-07-02 05:47:18.702 btags += '+' * lb
2025-07-02 05:47:18.711 elif tag == 'equal':
2025-07-02 05:47:18.722 atags += ' ' * la
2025-07-02 05:47:18.733 btags += ' ' * lb
2025-07-02 05:47:18.746 else:
2025-07-02 05:47:18.758 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:18.768 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:18.777 else:
2025-07-02 05:47:18.791 # the synch pair is identical
2025-07-02 05:47:18.804 yield '  ' + aelt
2025-07-02 05:47:18.818
2025-07-02 05:47:18.830 # pump out diffs from after the synch point
2025-07-02 05:47:18.840 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:18.848
2025-07-02 05:47:18.855 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:18.869 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:18.878
2025-07-02 05:47:18.888 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:18.899 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:18.907 alo = 120, ahi = 1101
2025-07-02 05:47:18.915 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:18.924 blo = 120, bhi = 1101
2025-07-02 05:47:18.935
2025-07-02 05:47:18.943 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:18.950 g = []
2025-07-02 05:47:18.960 if alo < ahi:
2025-07-02 05:47:18.969 if blo < bhi:
2025-07-02 05:47:18.977 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:18.984 else:
2025-07-02 05:47:18.991 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:18.997 elif blo < bhi:
2025-07-02 05:47:19.007 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:19.020
2025-07-02 05:47:19.033 >       yield from g
2025-07-02 05:47:19.041
2025-07-02 05:47:19.048 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:19.054 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:19.060
2025-07-02 05:47:19.066 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:19.073 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:19.080 alo = 120, ahi = 1101
2025-07-02 05:47:19.089 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:19.096 blo = 120, bhi = 1101
2025-07-02 05:47:19.103
2025-07-02 05:47:19.110 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:19.122 r"""
2025-07-02 05:47:19.133 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:19.147 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:19.159 synch point, and intraline difference marking is done on the
2025-07-02 05:47:19.168 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:19.176
2025-07-02 05:47:19.181 Example:
2025-07-02 05:47:19.191
2025-07-02 05:47:19.201 >>> d = Differ()
2025-07-02 05:47:19.207 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:19.214 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:19.222 >>> print(''.join(results), end="")
2025-07-02 05:47:19.234 - abcDefghiJkl
2025-07-02 05:47:19.249 + abcdefGhijkl
2025-07-02 05:47:19.262 """
2025-07-02 05:47:19.271
2025-07-02 05:47:19.285 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:19.296 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:19.304 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:19.311 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:19.317 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:19.324
2025-07-02 05:47:19.330 # search for the pair that matches best without being identical
2025-07-02 05:47:19.337 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:19.343 # on junk -- unless we have to)
2025-07-02 05:47:19.348 for j in range(blo, bhi):
2025-07-02 05:47:19.354 bj = b[j]
2025-07-02 05:47:19.365 cruncher.set_seq2(bj)
2025-07-02 05:47:19.373 for i in range(alo, ahi):
2025-07-02 05:47:19.380 ai = a[i]
2025-07-02 05:47:19.386 if ai == bj:
2025-07-02 05:47:19.392 if eqi is None:
2025-07-02 05:47:19.398 eqi, eqj = i, j
2025-07-02 05:47:19.405 continue
2025-07-02 05:47:19.411 cruncher.set_seq1(ai)
2025-07-02 05:47:19.419 # computing similarity is expensive, so use the quick
2025-07-02 05:47:19.426 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:19.437 # compares by a factor of 3.
2025-07-02 05:47:19.448 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:19.456 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:19.462 # of the computation is cached by cruncher
2025-07-02 05:47:19.468 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:19.474 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:19.479 cruncher.ratio() > best_ratio:
2025-07-02 05:47:19.483 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:19.488 if best_ratio < cutoff:
2025-07-02 05:47:19.493 # no non-identical "pretty close" pair
2025-07-02 05:47:19.498 if eqi is None:
2025-07-02 05:47:19.503 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:19.508 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:19.513 return
2025-07-02 05:47:19.518 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:19.523 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:19.527 else:
2025-07-02 05:47:19.534 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:19.539 eqi = None
2025-07-02 05:47:19.545
2025-07-02 05:47:19.551 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:19.556 # identical
2025-07-02 05:47:19.560
2025-07-02 05:47:19.565 # pump out diffs from before the synch point
2025-07-02 05:47:19.570 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:19.574
2025-07-02 05:47:19.579 # do intraline marking on the synch pair
2025-07-02 05:47:19.584 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:19.589 if eqi is None:
2025-07-02 05:47:19.598 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:19.611 atags = btags = ""
2025-07-02 05:47:19.623 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:19.633 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:19.643 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:19.651 if tag == 'replace':
2025-07-02 05:47:19.660 atags += '^' * la
2025-07-02 05:47:19.673 btags += '^' * lb
2025-07-02 05:47:19.685 elif tag == 'delete':
2025-07-02 05:47:19.694 atags += '-' * la
2025-07-02 05:47:19.703 elif tag == 'insert':
2025-07-02 05:47:19.711 btags += '+' * lb
2025-07-02 05:47:19.718 elif tag == 'equal':
2025-07-02 05:47:19.725 atags += ' ' * la
2025-07-02 05:47:19.731 btags += ' ' * lb
2025-07-02 05:47:19.738 else:
2025-07-02 05:47:19.750 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:19.759 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:19.765 else:
2025-07-02 05:47:19.772 # the synch pair is identical
2025-07-02 05:47:19.779 yield '  ' + aelt
2025-07-02 05:47:19.785
2025-07-02 05:47:19.796 # pump out diffs from after the synch point
2025-07-02 05:47:19.806 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:19.817
2025-07-02 05:47:19.832 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:19.842 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:19.848
2025-07-02 05:47:19.854 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:19.861 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:19.867 alo = 121, ahi = 1101
2025-07-02 05:47:19.880 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:19.892 blo = 121, bhi = 1101
2025-07-02 05:47:19.904
2025-07-02 05:47:19.917 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:19.928 g = []
2025-07-02 05:47:19.937 if alo < ahi:
2025-07-02 05:47:19.948 if blo < bhi:
2025-07-02 05:47:19.958 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:19.969 else:
2025-07-02 05:47:19.977 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:19.983 elif blo < bhi:
2025-07-02 05:47:19.989 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:19.996
2025-07-02 05:47:20.002 >       yield from g
2025-07-02 05:47:20.007
2025-07-02 05:47:20.015 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:20.027 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:20.036
2025-07-02 05:47:20.045 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:20.054 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:20.062 alo = 121, ahi = 1101
2025-07-02 05:47:20.070 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:20.077 blo = 121, bhi = 1101
2025-07-02 05:47:20.083
2025-07-02 05:47:20.090 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:20.096 r"""
2025-07-02 05:47:20.104 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:20.115 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:20.124 synch point, and intraline difference marking is done on the
2025-07-02 05:47:20.131 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:20.138
2025-07-02 05:47:20.146 Example:
2025-07-02 05:47:20.157
2025-07-02 05:47:20.167 >>> d = Differ()
2025-07-02 05:47:20.182 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:20.194 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:20.207 >>> print(''.join(results), end="")
2025-07-02 05:47:20.220 - abcDefghiJkl
2025-07-02 05:47:20.245 + abcdefGhijkl
2025-07-02 05:47:20.260 """
2025-07-02 05:47:20.266
2025-07-02 05:47:20.279 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:20.293 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:20.304 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:20.313 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:20.320 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:20.326
2025-07-02 05:47:20.339 # search for the pair that matches best without being identical
2025-07-02 05:47:20.352 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:20.365 # on junk -- unless we have to)
2025-07-02 05:47:20.375 for j in range(blo, bhi):
2025-07-02 05:47:20.385 bj = b[j]
2025-07-02 05:47:20.392 cruncher.set_seq2(bj)
2025-07-02 05:47:20.406 for i in range(alo, ahi):
2025-07-02 05:47:20.418 ai = a[i]
2025-07-02 05:47:20.427 if ai == bj:
2025-07-02 05:47:20.439 if eqi is None:
2025-07-02 05:47:20.449 eqi, eqj = i, j
2025-07-02 05:47:20.462 continue
2025-07-02 05:47:20.473 cruncher.set_seq1(ai)
2025-07-02 05:47:20.485 # computing similarity is expensive, so use the quick
2025-07-02 05:47:20.494 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:20.502 # compares by a factor of 3.
2025-07-02 05:47:20.510 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:20.519 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:20.530 # of the computation is cached by cruncher
2025-07-02 05:47:20.540 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:20.552 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:20.562 cruncher.ratio() > best_ratio:
2025-07-02 05:47:20.571 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:20.578 if best_ratio < cutoff:
2025-07-02 05:47:20.588 # no non-identical "pretty close" pair
2025-07-02 05:47:20.597 if eqi is None:
2025-07-02 05:47:20.603 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:20.609 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:20.615 return
2025-07-02 05:47:20.622 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:20.629 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:20.640 else:
2025-07-02 05:47:20.654 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:20.665 eqi = None
2025-07-02 05:47:20.678
2025-07-02 05:47:20.688 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:20.696 # identical
2025-07-02 05:47:20.703
2025-07-02 05:47:20.709 # pump out diffs from before the synch point
2025-07-02 05:47:20.715 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:20.720
2025-07-02 05:47:20.726 # do intraline marking on the synch pair
2025-07-02 05:47:20.736 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:20.746 if eqi is None:
2025-07-02 05:47:20.755 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:20.763 atags = btags = ""
2025-07-02 05:47:20.770 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:20.780 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:20.787 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:20.794 if tag == 'replace':
2025-07-02 05:47:20.803 atags += '^' * la
2025-07-02 05:47:20.814 btags += '^' * lb
2025-07-02 05:47:20.821 elif tag == 'delete':
2025-07-02 05:47:20.828 atags += '-' * la
2025-07-02 05:47:20.834 elif tag == 'insert':
2025-07-02 05:47:20.846 btags += '+' * lb
2025-07-02 05:47:20.857 elif tag == 'equal':
2025-07-02 05:47:20.867 atags += ' ' * la
2025-07-02 05:47:20.878 btags += ' ' * lb
2025-07-02 05:47:20.888 else:
2025-07-02 05:47:20.896 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:20.903 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:20.912 else:
2025-07-02 05:47:20.925 # the synch pair is identical
2025-07-02 05:47:20.933 yield '  ' + aelt
2025-07-02 05:47:20.939
2025-07-02 05:47:20.947 # pump out diffs from after the synch point
2025-07-02 05:47:20.960 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:20.968
2025-07-02 05:47:20.976 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:20.983 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:20.992
2025-07-02 05:47:21.003 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:21.012 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:21.018 alo = 122, ahi = 1101
2025-07-02 05:47:21.026 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:21.038 blo = 122, bhi = 1101
2025-07-02 05:47:21.052
2025-07-02 05:47:21.063 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:21.077 g = []
2025-07-02 05:47:21.087 if alo < ahi:
2025-07-02 05:47:21.095 if blo < bhi:
2025-07-02 05:47:21.102 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:21.107 else:
2025-07-02 05:47:21.112 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:21.118 elif blo < bhi:
2025-07-02 05:47:21.125 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:21.131
2025-07-02 05:47:21.137 >       yield from g
2025-07-02 05:47:21.147
2025-07-02 05:47:21.162 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:21.172 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:21.179
2025-07-02 05:47:21.186 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:21.195 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:21.207 alo = 122, ahi = 1101
2025-07-02 05:47:21.218 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:21.226 blo = 122, bhi = 1101
2025-07-02 05:47:21.240
2025-07-02 05:47:21.253 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:21.265 r"""
2025-07-02 05:47:21.276 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:21.285 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:21.292 synch point, and intraline difference marking is done on the
2025-07-02 05:47:21.299 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:21.308
2025-07-02 05:47:21.319 Example:
2025-07-02 05:47:21.329
2025-07-02 05:47:21.342 >>> d = Differ()
2025-07-02 05:47:21.354 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:21.364 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:21.371 >>> print(''.join(results), end="")
2025-07-02 05:47:21.377 - abcDefghiJkl
2025-07-02 05:47:21.386 + abcdefGhijkl
2025-07-02 05:47:21.397 """
2025-07-02 05:47:21.404
2025-07-02 05:47:21.411 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:21.418 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:21.429 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:21.438 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:21.445 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:21.451
2025-07-02 05:47:21.458 # search for the pair that matches best without being identical
2025-07-02 05:47:21.467 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:21.474 # on junk -- unless we have to)
2025-07-02 05:47:21.483 for j in range(blo, bhi):
2025-07-02 05:47:21.490 bj = b[j]
2025-07-02 05:47:21.496 cruncher.set_seq2(bj)
2025-07-02 05:47:21.502 for i in range(alo, ahi):
2025-07-02 05:47:21.511 ai = a[i]
2025-07-02 05:47:21.517 if ai == bj:
2025-07-02 05:47:21.524 if eqi is None:
2025-07-02 05:47:21.531 eqi, eqj = i, j
2025-07-02 05:47:21.541 continue
2025-07-02 05:47:21.551 cruncher.set_seq1(ai)
2025-07-02 05:47:21.561 # computing similarity is expensive, so use the quick
2025-07-02 05:47:21.569 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:21.582 # compares by a factor of 3.
2025-07-02 05:47:21.593 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:21.603 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:21.613 # of the computation is cached by cruncher
2025-07-02 05:47:21.621 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:21.627 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:21.632 cruncher.ratio() > best_ratio:
2025-07-02 05:47:21.636 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:21.642 if best_ratio < cutoff:
2025-07-02 05:47:21.650 # no non-identical "pretty close" pair
2025-07-02 05:47:21.660 if eqi is None:
2025-07-02 05:47:21.666 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:21.673 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:21.679 return
2025-07-02 05:47:21.685 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:21.691 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:21.703 else:
2025-07-02 05:47:21.715 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:21.727 eqi = None
2025-07-02 05:47:21.739
2025-07-02 05:47:21.750 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:21.760 # identical
2025-07-02 05:47:21.767
2025-07-02 05:47:21.778 # pump out diffs from before the synch point
2025-07-02 05:47:21.791 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:21.800
2025-07-02 05:47:21.807 # do intraline marking on the synch pair
2025-07-02 05:47:21.814 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:21.820 if eqi is None:
2025-07-02 05:47:21.827 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:21.833 atags = btags = ""
2025-07-02 05:47:21.839 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:21.852 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:21.860 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:21.867 if tag == 'replace':
2025-07-02 05:47:21.874 atags += '^' * la
2025-07-02 05:47:21.880 btags += '^' * lb
2025-07-02 05:47:21.886 elif tag == 'delete':
2025-07-02 05:47:21.897 atags += '-' * la
2025-07-02 05:47:21.907 elif tag == 'insert':
2025-07-02 05:47:21.915 btags += '+' * lb
2025-07-02 05:47:21.923 elif tag == 'equal':
2025-07-02 05:47:21.934 atags += ' ' * la
2025-07-02 05:47:21.943 btags += ' ' * lb
2025-07-02 05:47:21.951 else:
2025-07-02 05:47:21.959 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:21.971 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:21.984 else:
2025-07-02 05:47:21.994 # the synch pair is identical
2025-07-02 05:47:22.006 yield '  ' + aelt
2025-07-02 05:47:22.019
2025-07-02 05:47:22.028 # pump out diffs from after the synch point
2025-07-02 05:47:22.036 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:22.043
2025-07-02 05:47:22.051 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:22.063 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:22.071
2025-07-02 05:47:22.078 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:22.087 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:22.093 alo = 123, ahi = 1101
2025-07-02 05:47:22.102 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:22.119 blo = 123, bhi = 1101
2025-07-02 05:47:22.129
2025-07-02 05:47:22.137 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:22.145 g = []
2025-07-02 05:47:22.151 if alo < ahi:
2025-07-02 05:47:22.157 if blo < bhi:
2025-07-02 05:47:22.163 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:22.168 else:
2025-07-02 05:47:22.172 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:22.178 elif blo < bhi:
2025-07-02 05:47:22.183 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:22.188
2025-07-02 05:47:22.194 >       yield from g
2025-07-02 05:47:22.200
2025-07-02 05:47:22.207 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:22.213 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:22.219
2025-07-02 05:47:22.225 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:22.233 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:22.238 alo = 123, ahi = 1101
2025-07-02 05:47:22.247 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:22.257 blo = 123, bhi = 1101
2025-07-02 05:47:22.267
2025-07-02 05:47:22.277 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:22.284 r"""
2025-07-02 05:47:22.291 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:22.296 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:22.303 synch point, and intraline difference marking is done on the
2025-07-02 05:47:22.310 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:22.316
2025-07-02 05:47:22.322 Example:
2025-07-02 05:47:22.334
2025-07-02 05:47:22.344 >>> d = Differ()
2025-07-02 05:47:22.351 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:22.359 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:22.365 >>> print(''.join(results), end="")
2025-07-02 05:47:22.372 - abcDefghiJkl
2025-07-02 05:47:22.384 + abcdefGhijkl
2025-07-02 05:47:22.396 """
2025-07-02 05:47:22.403
2025-07-02 05:47:22.419 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:22.430 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:22.442 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:22.455 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:22.468 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:22.476
2025-07-02 05:47:22.484 # search for the pair that matches best without being identical
2025-07-02 05:47:22.491 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:22.507 # on junk -- unless we have to)
2025-07-02 05:47:22.516 for j in range(blo, bhi):
2025-07-02 05:47:22.525 bj = b[j]
2025-07-02 05:47:22.533 cruncher.set_seq2(bj)
2025-07-02 05:47:22.539 for i in range(alo, ahi):
2025-07-02 05:47:22.551 ai = a[i]
2025-07-02 05:47:22.561 if ai == bj:
2025-07-02 05:47:22.569 if eqi is None:
2025-07-02 05:47:22.576 eqi, eqj = i, j
2025-07-02 05:47:22.583 continue
2025-07-02 05:47:22.597 cruncher.set_seq1(ai)
2025-07-02 05:47:22.611 # computing similarity is expensive, so use the quick
2025-07-02 05:47:22.623 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:22.632 # compares by a factor of 3.
2025-07-02 05:47:22.640 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:22.646 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:22.654 # of the computation is cached by cruncher
2025-07-02 05:47:22.662 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:22.673 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:22.686 cruncher.ratio() > best_ratio:
2025-07-02 05:47:22.696 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:22.704 if best_ratio < cutoff:
2025-07-02 05:47:22.711 # no non-identical "pretty close" pair
2025-07-02 05:47:22.718 if eqi is None:
2025-07-02 05:47:22.724 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:22.731 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:22.739 return
2025-07-02 05:47:22.750 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:22.758 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:22.767 else:
2025-07-02 05:47:22.778 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:22.787 eqi = None
2025-07-02 05:47:22.795
2025-07-02 05:47:22.803 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:22.812 # identical
2025-07-02 05:47:22.824
2025-07-02 05:47:22.833 # pump out diffs from before the synch point
2025-07-02 05:47:22.840 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:22.847
2025-07-02 05:47:22.856 # do intraline marking on the synch pair
2025-07-02 05:47:22.868 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:22.877 if eqi is None:
2025-07-02 05:47:22.884 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:22.891 atags = btags = ""
2025-07-02 05:47:22.897 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:22.903 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:22.911 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:22.916 if tag == 'replace':
2025-07-02 05:47:22.928 atags += '^' * la
2025-07-02 05:47:22.937 btags += '^' * lb
2025-07-02 05:47:22.943 elif tag == 'delete':
2025-07-02 05:47:22.949 atags += '-' * la
2025-07-02 05:47:22.954 elif tag == 'insert':
2025-07-02 05:47:22.965 btags += '+' * lb
2025-07-02 05:47:22.974 elif tag == 'equal':
2025-07-02 05:47:22.982 atags += ' ' * la
2025-07-02 05:47:22.991 btags += ' ' * lb
2025-07-02 05:47:22.998 else:
2025-07-02 05:47:23.004 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:23.012 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:23.019 else:
2025-07-02 05:47:23.026 # the synch pair is identical
2025-07-02 05:47:23.035 yield '  ' + aelt
2025-07-02 05:47:23.048
2025-07-02 05:47:23.057 # pump out diffs from after the synch point
2025-07-02 05:47:23.065 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:23.077
2025-07-02 05:47:23.089 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:23.101 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:23.114
2025-07-02 05:47:23.127 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:23.135 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:23.148 alo = 124, ahi = 1101
2025-07-02 05:47:23.160 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:23.170 blo = 124, bhi = 1101
2025-07-02 05:47:23.177
2025-07-02 05:47:23.191 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:23.201 g = []
2025-07-02 05:47:23.210 if alo < ahi:
2025-07-02 05:47:23.222 if blo < bhi:
2025-07-02 05:47:23.232 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:23.238 else:
2025-07-02 05:47:23.245 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:23.250 elif blo < bhi:
2025-07-02 05:47:23.257 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:23.263
2025-07-02 05:47:23.270 >       yield from g
2025-07-02 05:47:23.277
2025-07-02 05:47:23.284 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:23.291 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:23.298
2025-07-02 05:47:23.305 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:23.313 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:23.325 alo = 124, ahi = 1101
2025-07-02 05:47:23.335 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:23.344 blo = 124, bhi = 1101
2025-07-02 05:47:23.351
2025-07-02 05:47:23.357 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:23.364 r"""
2025-07-02 05:47:23.370 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:23.381 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:23.391 synch point, and intraline difference marking is done on the
2025-07-02 05:47:23.404 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:23.416
2025-07-02 05:47:23.430 Example:
2025-07-02 05:47:23.442
2025-07-02 05:47:23.451 >>> d = Differ()
2025-07-02 05:47:23.458 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:23.473 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:23.483 >>> print(''.join(results), end="")
2025-07-02 05:47:23.491 - abcDefghiJkl
2025-07-02 05:47:23.504 + abcdefGhijkl
2025-07-02 05:47:23.524 """
2025-07-02 05:47:23.539
2025-07-02 05:47:23.552 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:23.564 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:23.574 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:23.587 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:23.600 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:23.611
2025-07-02 05:47:23.620 # search for the pair that matches best without being identical
2025-07-02 05:47:23.631 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:23.644 # on junk -- unless we have to)
2025-07-02 05:47:23.657 for j in range(blo, bhi):
2025-07-02 05:47:23.668 bj = b[j]
2025-07-02 05:47:23.681 cruncher.set_seq2(bj)
2025-07-02 05:47:23.694 for i in range(alo, ahi):
2025-07-02 05:47:23.704 ai = a[i]
2025-07-02 05:47:23.712 if ai == bj:
2025-07-02 05:47:23.720 if eqi is None:
2025-07-02 05:47:23.727 eqi, eqj = i, j
2025-07-02 05:47:23.735 continue
2025-07-02 05:47:23.747 cruncher.set_seq1(ai)
2025-07-02 05:47:23.756 # computing similarity is expensive, so use the quick
2025-07-02 05:47:23.762 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:23.773 # compares by a factor of 3.
2025-07-02 05:47:23.784 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:23.796 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:23.806 # of the computation is cached by cruncher
2025-07-02 05:47:23.818 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:23.827 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:23.834 cruncher.ratio() > best_ratio:
2025-07-02 05:47:23.845 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:23.854 if best_ratio < cutoff:
2025-07-02 05:47:23.861 # no non-identical "pretty close" pair
2025-07-02 05:47:23.868 if eqi is None:
2025-07-02 05:47:23.875 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:23.881 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:23.887 return
2025-07-02 05:47:23.893 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:23.899 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:23.905 else:
2025-07-02 05:47:23.911 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:23.920 eqi = None
2025-07-02 05:47:23.930
2025-07-02 05:47:23.937 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:23.944 # identical
2025-07-02 05:47:23.951
2025-07-02 05:47:23.958 # pump out diffs from before the synch point
2025-07-02 05:47:23.968 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:23.977
2025-07-02 05:47:23.986 # do intraline marking on the synch pair
2025-07-02 05:47:23.997 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:24.008 if eqi is None:
2025-07-02 05:47:24.017 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:24.024 atags = btags = ""
2025-07-02 05:47:24.031 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:24.042 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:24.053 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:24.063 if tag == 'replace':
2025-07-02 05:47:24.073 atags += '^' * la
2025-07-02 05:47:24.086 btags += '^' * lb
2025-07-02 05:47:24.096 elif tag == 'delete':
2025-07-02 05:47:24.104 atags += '-' * la
2025-07-02 05:47:24.113 elif tag == 'insert':
2025-07-02 05:47:24.119 btags += '+' * lb
2025-07-02 05:47:24.132 elif tag == 'equal':
2025-07-02 05:47:24.143 atags += ' ' * la
2025-07-02 05:47:24.154 btags += ' ' * lb
2025-07-02 05:47:24.165 else:
2025-07-02 05:47:24.174 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:24.181 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:24.193 else:
2025-07-02 05:47:24.203 # the synch pair is identical
2025-07-02 05:47:24.210 yield '  ' + aelt
2025-07-02 05:47:24.216
2025-07-02 05:47:24.226 # pump out diffs from after the synch point
2025-07-02 05:47:24.238 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:24.250
2025-07-02 05:47:24.261 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:24.272 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:24.284
2025-07-02 05:47:24.294 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:24.304 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:24.311 alo = 125, ahi = 1101
2025-07-02 05:47:24.317 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:24.323 blo = 125, bhi = 1101
2025-07-02 05:47:24.329
2025-07-02 05:47:24.334 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:24.339 g = []
2025-07-02 05:47:24.344 if alo < ahi:
2025-07-02 05:47:24.349 if blo < bhi:
2025-07-02 05:47:24.358 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:24.367 else:
2025-07-02 05:47:24.374 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:24.383 elif blo < bhi:
2025-07-02 05:47:24.394 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:24.402
2025-07-02 05:47:24.411 >       yield from g
2025-07-02 05:47:24.420
2025-07-02 05:47:24.428 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:24.435 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:24.440
2025-07-02 05:47:24.451 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:24.465 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:24.473 alo = 125, ahi = 1101
2025-07-02 05:47:24.485 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:24.494 blo = 125, bhi = 1101
2025-07-02 05:47:24.505
2025-07-02 05:47:24.516 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:24.525 r"""
2025-07-02 05:47:24.534 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:24.546 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:24.558 synch point, and intraline difference marking is done on the
2025-07-02 05:47:24.567 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:24.575
2025-07-02 05:47:24.583 Example:
2025-07-02 05:47:24.595
2025-07-02 05:47:24.605 >>> d = Differ()
2025-07-02 05:47:24.613 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:24.621 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:24.627 >>> print(''.join(results), end="")
2025-07-02 05:47:24.637 - abcDefghiJkl
2025-07-02 05:47:24.655 + abcdefGhijkl
2025-07-02 05:47:24.682 """
2025-07-02 05:47:24.692
2025-07-02 05:47:24.700 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:24.707 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:24.714 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:24.722 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:24.731 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:24.738
2025-07-02 05:47:24.749 # search for the pair that matches best without being identical
2025-07-02 05:47:24.760 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:24.769 # on junk -- unless we have to)
2025-07-02 05:47:24.777 for j in range(blo, bhi):
2025-07-02 05:47:24.783 bj = b[j]
2025-07-02 05:47:24.789 cruncher.set_seq2(bj)
2025-07-02 05:47:24.794 for i in range(alo, ahi):
2025-07-02 05:47:24.798 ai = a[i]
2025-07-02 05:47:24.803 if ai == bj:
2025-07-02 05:47:24.809 if eqi is None:
2025-07-02 05:47:24.814 eqi, eqj = i, j
2025-07-02 05:47:24.818 continue
2025-07-02 05:47:24.823 cruncher.set_seq1(ai)
2025-07-02 05:47:24.829 # computing similarity is expensive, so use the quick
2025-07-02 05:47:24.835 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:24.842 # compares by a factor of 3.
2025-07-02 05:47:24.849 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:24.856 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:24.862 # of the computation is cached by cruncher
2025-07-02 05:47:24.871 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:24.882 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:24.893 cruncher.ratio() > best_ratio:
2025-07-02 05:47:24.907 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:24.918 if best_ratio < cutoff:
2025-07-02 05:47:24.926 # no non-identical "pretty close" pair
2025-07-02 05:47:24.938 if eqi is None:
2025-07-02 05:47:24.948 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:24.957 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:24.963 return
2025-07-02 05:47:24.970 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:24.976 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:24.983 else:
2025-07-02 05:47:24.995 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:25.004 eqi = None
2025-07-02 05:47:25.012
2025-07-02 05:47:25.020 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:25.027 # identical
2025-07-02 05:47:25.035
2025-07-02 05:47:25.046 # pump out diffs from before the synch point
2025-07-02 05:47:25.058 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:25.075
2025-07-02 05:47:25.085 # do intraline marking on the synch pair
2025-07-02 05:47:25.093 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:25.100 if eqi is None:
2025-07-02 05:47:25.105 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:25.110 atags = btags = ""
2025-07-02 05:47:25.116 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:25.123 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:25.133 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:25.140 if tag == 'replace':
2025-07-02 05:47:25.147 atags += '^' * la
2025-07-02 05:47:25.158 btags += '^' * lb
2025-07-02 05:47:25.171 elif tag == 'delete':
2025-07-02 05:47:25.184 atags += '-' * la
2025-07-02 05:47:25.195 elif tag == 'insert':
2025-07-02 05:47:25.208 btags += '+' * lb
2025-07-02 05:47:25.217 elif tag == 'equal':
2025-07-02 05:47:25.228 atags += ' ' * la
2025-07-02 05:47:25.240 btags += ' ' * lb
2025-07-02 05:47:25.250 else:
2025-07-02 05:47:25.258 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:25.265 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:25.275 else:
2025-07-02 05:47:25.286 # the synch pair is identical
2025-07-02 05:47:25.293 yield '  ' + aelt
2025-07-02 05:47:25.304
2025-07-02 05:47:25.316 # pump out diffs from after the synch point
2025-07-02 05:47:25.331 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:25.339
2025-07-02 05:47:25.347 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:25.354 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:25.365
2025-07-02 05:47:25.376 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:25.385 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:25.392 alo = 126, ahi = 1101
2025-07-02 05:47:25.399 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:25.408 blo = 126, bhi = 1101
2025-07-02 05:47:25.421
2025-07-02 05:47:25.431 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:25.439 g = []
2025-07-02 05:47:25.447 if alo < ahi:
2025-07-02 05:47:25.459 if blo < bhi:
2025-07-02 05:47:25.467 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:25.474 else:
2025-07-02 05:47:25.483 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:25.490 elif blo < bhi:
2025-07-02 05:47:25.496 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:25.502
2025-07-02 05:47:25.512 >       yield from g
2025-07-02 05:47:25.522
2025-07-02 05:47:25.530 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:25.541 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:25.552
2025-07-02 05:47:25.566 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:25.577 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:25.584 alo = 126, ahi = 1101
2025-07-02 05:47:25.592 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:25.599 blo = 126, bhi = 1101
2025-07-02 05:47:25.604
2025-07-02 05:47:25.611 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:25.617 r"""
2025-07-02 05:47:25.623 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:25.630 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:25.642 synch point, and intraline difference marking is done on the
2025-07-02 05:47:25.655 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:25.666
2025-07-02 05:47:25.673 Example:
2025-07-02 05:47:25.680
2025-07-02 05:47:25.687 >>> d = Differ()
2025-07-02 05:47:25.693 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:25.706 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:25.716 >>> print(''.join(results), end="")
2025-07-02 05:47:25.722 - abcDefghiJkl
2025-07-02 05:47:25.734 + abcdefGhijkl
2025-07-02 05:47:25.749 """
2025-07-02 05:47:25.755
2025-07-02 05:47:25.765 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:25.774 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:25.780 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:25.786 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:25.792 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:25.798
2025-07-02 05:47:25.809 # search for the pair that matches best without being identical
2025-07-02 05:47:25.819 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:25.826 # on junk -- unless we have to)
2025-07-02 05:47:25.834 for j in range(blo, bhi):
2025-07-02 05:47:25.846 bj = b[j]
2025-07-02 05:47:25.857 cruncher.set_seq2(bj)
2025-07-02 05:47:25.869 for i in range(alo, ahi):
2025-07-02 05:47:25.879 ai = a[i]
2025-07-02 05:47:25.886 if ai == bj:
2025-07-02 05:47:25.893 if eqi is None:
2025-07-02 05:47:25.902 eqi, eqj = i, j
2025-07-02 05:47:25.914 continue
2025-07-02 05:47:25.923 cruncher.set_seq1(ai)
2025-07-02 05:47:25.934 # computing similarity is expensive, so use the quick
2025-07-02 05:47:25.944 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:25.952 # compares by a factor of 3.
2025-07-02 05:47:25.959 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:25.966 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:25.981 # of the computation is cached by cruncher
2025-07-02 05:47:25.990 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:26.000 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:26.013 cruncher.ratio() > best_ratio:
2025-07-02 05:47:26.024 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:26.031 if best_ratio < cutoff:
2025-07-02 05:47:26.043 # no non-identical "pretty close" pair
2025-07-02 05:47:26.056 if eqi is None:
2025-07-02 05:47:26.070 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:26.079 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:26.088 return
2025-07-02 05:47:26.101 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:26.111 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:26.118 else:
2025-07-02 05:47:26.127 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:26.139 eqi = None
2025-07-02 05:47:26.150
2025-07-02 05:47:26.160 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:26.171 # identical
2025-07-02 05:47:26.183
2025-07-02 05:47:26.197 # pump out diffs from before the synch point
2025-07-02 05:47:26.210 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:26.219
2025-07-02 05:47:26.227 # do intraline marking on the synch pair
2025-07-02 05:47:26.234 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:26.244 if eqi is None:
2025-07-02 05:47:26.254 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:26.260 atags = btags = ""
2025-07-02 05:47:26.265 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:26.271 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:26.281 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:26.289 if tag == 'replace':
2025-07-02 05:47:26.297 atags += '^' * la
2025-07-02 05:47:26.304 btags += '^' * lb
2025-07-02 05:47:26.311 elif tag == 'delete':
2025-07-02 05:47:26.318 atags += '-' * la
2025-07-02 05:47:26.328 elif tag == 'insert':
2025-07-02 05:47:26.339 btags += '+' * lb
2025-07-02 05:47:26.347 elif tag == 'equal':
2025-07-02 05:47:26.356 atags += ' ' * la
2025-07-02 05:47:26.367 btags += ' ' * lb
2025-07-02 05:47:26.378 else:
2025-07-02 05:47:26.388 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:26.396 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:26.405 else:
2025-07-02 05:47:26.416 # the synch pair is identical
2025-07-02 05:47:26.427 yield '  ' + aelt
2025-07-02 05:47:26.435
2025-07-02 05:47:26.443 # pump out diffs from after the synch point
2025-07-02 05:47:26.451 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:26.458
2025-07-02 05:47:26.465 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:26.471 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:26.477
2025-07-02 05:47:26.483 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:26.490 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:26.503 alo = 127, ahi = 1101
2025-07-02 05:47:26.515 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:26.528 blo = 127, bhi = 1101
2025-07-02 05:47:26.538
2025-07-02 05:47:26.554 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:26.566 g = []
2025-07-02 05:47:26.575 if alo < ahi:
2025-07-02 05:47:26.585 if blo < bhi:
2025-07-02 05:47:26.594 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:26.608 else:
2025-07-02 05:47:26.617 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:26.625 elif blo < bhi:
2025-07-02 05:47:26.633 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:26.639
2025-07-02 05:47:26.647 >       yield from g
2025-07-02 05:47:26.658
2025-07-02 05:47:26.666 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:26.680 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:26.692
2025-07-02 05:47:26.702 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:26.711 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:26.718 alo = 127, ahi = 1101
2025-07-02 05:47:26.727 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:26.741 blo = 127, bhi = 1101
2025-07-02 05:47:26.750
2025-07-02 05:47:26.757 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:26.764 r"""
2025-07-02 05:47:26.771 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:26.778 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:26.788 synch point, and intraline difference marking is done on the
2025-07-02 05:47:26.795 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:26.807
2025-07-02 05:47:26.818 Example:
2025-07-02 05:47:26.830
2025-07-02 05:47:26.841 >>> d = Differ()
2025-07-02 05:47:26.853 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:26.864 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:26.872 >>> print(''.join(results), end="")
2025-07-02 05:47:26.879 - abcDefghiJkl
2025-07-02 05:47:26.896 + abcdefGhijkl
2025-07-02 05:47:26.919 """
2025-07-02 05:47:26.931
2025-07-02 05:47:26.940 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:26.948 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:26.955 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:26.968 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:26.979 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:26.987
2025-07-02 05:47:26.998 # search for the pair that matches best without being identical
2025-07-02 05:47:27.009 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:27.021 # on junk -- unless we have to)
2025-07-02 05:47:27.031 for j in range(blo, bhi):
2025-07-02 05:47:27.044 bj = b[j]
2025-07-02 05:47:27.055 cruncher.set_seq2(bj)
2025-07-02 05:47:27.064 for i in range(alo, ahi):
2025-07-02 05:47:27.071 ai = a[i]
2025-07-02 05:47:27.077 if ai == bj:
2025-07-02 05:47:27.083 if eqi is None:
2025-07-02 05:47:27.089 eqi, eqj = i, j
2025-07-02 05:47:27.096 continue
2025-07-02 05:47:27.103 cruncher.set_seq1(ai)
2025-07-02 05:47:27.110 # computing similarity is expensive, so use the quick
2025-07-02 05:47:27.120 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:27.127 # compares by a factor of 3.
2025-07-02 05:47:27.134 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:27.140 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:27.147 # of the computation is cached by cruncher
2025-07-02 05:47:27.153 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:27.159 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:27.165 cruncher.ratio() > best_ratio:
2025-07-02 05:47:27.171 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:27.179 if best_ratio < cutoff:
2025-07-02 05:47:27.189 # no non-identical "pretty close" pair
2025-07-02 05:47:27.197 if eqi is None:
2025-07-02 05:47:27.205 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:27.213 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:27.224 return
2025-07-02 05:47:27.237 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:27.247 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:27.255 else:
2025-07-02 05:47:27.264 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:27.270 eqi = None
2025-07-02 05:47:27.276
2025-07-02 05:47:27.291 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:27.300 # identical
2025-07-02 05:47:27.311
2025-07-02 05:47:27.323 # pump out diffs from before the synch point
2025-07-02 05:47:27.333 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:27.341
2025-07-02 05:47:27.348 # do intraline marking on the synch pair
2025-07-02 05:47:27.356 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:27.363 if eqi is None:
2025-07-02 05:47:27.370 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:27.376 atags = btags = ""
2025-07-02 05:47:27.383 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:27.391 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:27.405 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:27.416 if tag == 'replace':
2025-07-02 05:47:27.423 atags += '^' * la
2025-07-02 05:47:27.430 btags += '^' * lb
2025-07-02 05:47:27.441 elif tag == 'delete':
2025-07-02 05:47:27.454 atags += '-' * la
2025-07-02 05:47:27.464 elif tag == 'insert':
2025-07-02 05:47:27.473 btags += '+' * lb
2025-07-02 05:47:27.487 elif tag == 'equal':
2025-07-02 05:47:27.500 atags += ' ' * la
2025-07-02 05:47:27.507 btags += ' ' * lb
2025-07-02 05:47:27.516 else:
2025-07-02 05:47:27.528 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:27.540 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:27.550 else:
2025-07-02 05:47:27.563 # the synch pair is identical
2025-07-02 05:47:27.573 yield '  ' + aelt
2025-07-02 05:47:27.581
2025-07-02 05:47:27.587 # pump out diffs from after the synch point
2025-07-02 05:47:27.594 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:27.602
2025-07-02 05:47:27.611 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:27.618 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:27.624
2025-07-02 05:47:27.631 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:27.640 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:27.647 alo = 128, ahi = 1101
2025-07-02 05:47:27.656 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:27.670 blo = 128, bhi = 1101
2025-07-02 05:47:27.678
2025-07-02 05:47:27.685 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:27.692 g = []
2025-07-02 05:47:27.699 if alo < ahi:
2025-07-02 05:47:27.706 if blo < bhi:
2025-07-02 05:47:27.717 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:27.729 else:
2025-07-02 05:47:27.742 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:27.754 elif blo < bhi:
2025-07-02 05:47:27.768 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:27.779
2025-07-02 05:47:27.788 >       yield from g
2025-07-02 05:47:27.797
2025-07-02 05:47:27.810 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:27.821 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:27.828
2025-07-02 05:47:27.835 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:27.847 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:27.858 alo = 128, ahi = 1101
2025-07-02 05:47:27.868 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:27.876 blo = 128, bhi = 1101
2025-07-02 05:47:27.883
2025-07-02 05:47:27.889 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:27.895 r"""
2025-07-02 05:47:27.901 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:27.912 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:27.927 synch point, and intraline difference marking is done on the
2025-07-02 05:47:27.937 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:27.948
2025-07-02 05:47:27.959 Example:
2025-07-02 05:47:27.969
2025-07-02 05:47:27.979 >>> d = Differ()
2025-07-02 05:47:27.989 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:27.997 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:28.006 >>> print(''.join(results), end="")
2025-07-02 05:47:28.013 - abcDefghiJkl
2025-07-02 05:47:28.026 + abcdefGhijkl
2025-07-02 05:47:28.038 """
2025-07-02 05:47:28.043
2025-07-02 05:47:28.050 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:28.062 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:28.072 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:28.082 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:28.094 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:28.105
2025-07-02 05:47:28.117 # search for the pair that matches best without being identical
2025-07-02 05:47:28.125 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:28.132 # on junk -- unless we have to)
2025-07-02 05:47:28.139 for j in range(blo, bhi):
2025-07-02 05:47:28.147 bj = b[j]
2025-07-02 05:47:28.160 cruncher.set_seq2(bj)
2025-07-02 05:47:28.173 for i in range(alo, ahi):
2025-07-02 05:47:28.181 ai = a[i]
2025-07-02 05:47:28.188 if ai == bj:
2025-07-02 05:47:28.195 if eqi is None:
2025-07-02 05:47:28.200 eqi, eqj = i, j
2025-07-02 05:47:28.205 continue
2025-07-02 05:47:28.209 cruncher.set_seq1(ai)
2025-07-02 05:47:28.214 # computing similarity is expensive, so use the quick
2025-07-02 05:47:28.219 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:28.224 # compares by a factor of 3.
2025-07-02 05:47:28.229 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:28.234 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:28.239 # of the computation is cached by cruncher
2025-07-02 05:47:28.244 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:28.249 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:28.254 cruncher.ratio() > best_ratio:
2025-07-02 05:47:28.259 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:28.264 if best_ratio < cutoff:
2025-07-02 05:47:28.269 # no non-identical "pretty close" pair
2025-07-02 05:47:28.273 if eqi is None:
2025-07-02 05:47:28.278 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:28.283 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:28.288 return
2025-07-02 05:47:28.293 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:28.298 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:28.303 else:
2025-07-02 05:47:28.308 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:28.313 eqi = None
2025-07-02 05:47:28.317
2025-07-02 05:47:28.322 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:28.334 # identical
2025-07-02 05:47:28.344
2025-07-02 05:47:28.353 # pump out diffs from before the synch point
2025-07-02 05:47:28.365 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:28.375
2025-07-02 05:47:28.383 # do intraline marking on the synch pair
2025-07-02 05:47:28.390 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:28.395 if eqi is None:
2025-07-02 05:47:28.400 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:28.405 atags = btags = ""
2025-07-02 05:47:28.410 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:28.414 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:28.419 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:28.424 if tag == 'replace':
2025-07-02 05:47:28.433 atags += '^' * la
2025-07-02 05:47:28.443 btags += '^' * lb
2025-07-02 05:47:28.452 elif tag == 'delete':
2025-07-02 05:47:28.459 atags += '-' * la
2025-07-02 05:47:28.466 elif tag == 'insert':
2025-07-02 05:47:28.471 btags += '+' * lb
2025-07-02 05:47:28.476 elif tag == 'equal':
2025-07-02 05:47:28.481 atags += ' ' * la
2025-07-02 05:47:28.485 btags += ' ' * lb
2025-07-02 05:47:28.490 else:
2025-07-02 05:47:28.497 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:28.506 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:28.515 else:
2025-07-02 05:47:28.523 # the synch pair is identical
2025-07-02 05:47:28.531 yield '  ' + aelt
2025-07-02 05:47:28.539
2025-07-02 05:47:28.552 # pump out diffs from after the synch point
2025-07-02 05:47:28.562 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:28.571
2025-07-02 05:47:28.578 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:28.586 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:28.594
2025-07-02 05:47:28.605 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:28.615 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:28.623 alo = 129, ahi = 1101
2025-07-02 05:47:28.630 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:28.637 blo = 129, bhi = 1101
2025-07-02 05:47:28.645
2025-07-02 05:47:28.658 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:28.668 g = []
2025-07-02 05:47:28.675 if alo < ahi:
2025-07-02 05:47:28.681 if blo < bhi:
2025-07-02 05:47:28.687 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:28.693 else:
2025-07-02 05:47:28.699 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:28.707 elif blo < bhi:
2025-07-02 05:47:28.716 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:28.724
2025-07-02 05:47:28.731 >       yield from g
2025-07-02 05:47:28.736
2025-07-02 05:47:28.742 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:28.753 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:28.763
2025-07-02 05:47:28.774 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:28.785 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:28.793 alo = 129, ahi = 1101
2025-07-02 05:47:28.805 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:28.814 blo = 129, bhi = 1101
2025-07-02 05:47:28.821
2025-07-02 05:47:28.827 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:28.833 r"""
2025-07-02 05:47:28.839 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:28.847 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:28.855 synch point, and intraline difference marking is done on the
2025-07-02 05:47:28.862 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:28.869
2025-07-02 05:47:28.876 Example:
2025-07-02 05:47:28.883
2025-07-02 05:47:28.890 >>> d = Differ()
2025-07-02 05:47:28.899 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:28.910 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:28.919 >>> print(''.join(results), end="")
2025-07-02 05:47:28.926 - abcDefghiJkl
2025-07-02 05:47:28.938 + abcdefGhijkl
2025-07-02 05:47:28.956 """
2025-07-02 05:47:28.962
2025-07-02 05:47:28.970 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:28.977 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:28.984 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:28.991 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:28.999 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:29.008
2025-07-02 05:47:29.017 # search for the pair that matches best without being identical
2025-07-02 05:47:29.024 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:29.031 # on junk -- unless we have to)
2025-07-02 05:47:29.039 for j in range(blo, bhi):
2025-07-02 05:47:29.045 bj = b[j]
2025-07-02 05:47:29.051 cruncher.set_seq2(bj)
2025-07-02 05:47:29.059 for i in range(alo, ahi):
2025-07-02 05:47:29.068 ai = a[i]
2025-07-02 05:47:29.077 if ai == bj:
2025-07-02 05:47:29.088 if eqi is None:
2025-07-02 05:47:29.097 eqi, eqj = i, j
2025-07-02 05:47:29.105 continue
2025-07-02 05:47:29.112 cruncher.set_seq1(ai)
2025-07-02 05:47:29.124 # computing similarity is expensive, so use the quick
2025-07-02 05:47:29.133 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:29.142 # compares by a factor of 3.
2025-07-02 05:47:29.155 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:29.169 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:29.182 # of the computation is cached by cruncher
2025-07-02 05:47:29.193 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:29.205 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:29.217 cruncher.ratio() > best_ratio:
2025-07-02 05:47:29.228 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:29.237 if best_ratio < cutoff:
2025-07-02 05:47:29.244 # no non-identical "pretty close" pair
2025-07-02 05:47:29.250 if eqi is None:
2025-07-02 05:47:29.259 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:29.270 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:29.278 return
2025-07-02 05:47:29.286 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:29.295 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:29.303 else:
2025-07-02 05:47:29.310 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:29.317 eqi = None
2025-07-02 05:47:29.322
2025-07-02 05:47:29.328 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:29.335 # identical
2025-07-02 05:47:29.342
2025-07-02 05:47:29.351 # pump out diffs from before the synch point
2025-07-02 05:47:29.363 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:29.374
2025-07-02 05:47:29.381 # do intraline marking on the synch pair
2025-07-02 05:47:29.386 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:29.392 if eqi is None:
2025-07-02 05:47:29.397 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:29.401 atags = btags = ""
2025-07-02 05:47:29.407 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:29.414 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:29.429 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:29.439 if tag == 'replace':
2025-07-02 05:47:29.447 atags += '^' * la
2025-07-02 05:47:29.455 btags += '^' * lb
2025-07-02 05:47:29.465 elif tag == 'delete':
2025-07-02 05:47:29.474 atags += '-' * la
2025-07-02 05:47:29.482 elif tag == 'insert':
2025-07-02 05:47:29.490 btags += '+' * lb
2025-07-02 05:47:29.501 elif tag == 'equal':
2025-07-02 05:47:29.511 atags += ' ' * la
2025-07-02 05:47:29.520 btags += ' ' * lb
2025-07-02 05:47:29.526 else:
2025-07-02 05:47:29.537 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:29.547 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:29.555 else:
2025-07-02 05:47:29.562 # the synch pair is identical
2025-07-02 05:47:29.569 yield '  ' + aelt
2025-07-02 05:47:29.575
2025-07-02 05:47:29.581 # pump out diffs from after the synch point
2025-07-02 05:47:29.592 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:29.601
2025-07-02 05:47:29.609 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:29.616 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:29.622
2025-07-02 05:47:29.635 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:29.644 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:29.650 alo = 130, ahi = 1101
2025-07-02 05:47:29.656 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:29.661 blo = 130, bhi = 1101
2025-07-02 05:47:29.667
2025-07-02 05:47:29.674 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:29.679 g = []
2025-07-02 05:47:29.685 if alo < ahi:
2025-07-02 05:47:29.691 if blo < bhi:
2025-07-02 05:47:29.701 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:29.710 else:
2025-07-02 05:47:29.716 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:29.722 elif blo < bhi:
2025-07-02 05:47:29.728 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:29.734
2025-07-02 05:47:29.739 >       yield from g
2025-07-02 05:47:29.743
2025-07-02 05:47:29.748 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:29.753 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:29.758
2025-07-02 05:47:29.763 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:29.768 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:29.773 alo = 130, ahi = 1101
2025-07-02 05:47:29.778 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:29.783 blo = 130, bhi = 1101
2025-07-02 05:47:29.788
2025-07-02 05:47:29.793 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:29.798 r"""
2025-07-02 05:47:29.803 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:29.808 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:29.815 synch point, and intraline difference marking is done on the
2025-07-02 05:47:29.823 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:29.832
2025-07-02 05:47:29.840 Example:
2025-07-02 05:47:29.846
2025-07-02 05:47:29.851 >>> d = Differ()
2025-07-02 05:47:29.856 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:29.861 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:29.866 >>> print(''.join(results), end="")
2025-07-02 05:47:29.871 - abcDefghiJkl
2025-07-02 05:47:29.882 + abcdefGhijkl
2025-07-02 05:47:29.894 """
2025-07-02 05:47:29.902
2025-07-02 05:47:29.909 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:29.915 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:29.920 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:29.925 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:29.931 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:29.937
2025-07-02 05:47:29.943 # search for the pair that matches best without being identical
2025-07-02 05:47:29.950 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:29.957 # on junk -- unless we have to)
2025-07-02 05:47:29.964 for j in range(blo, bhi):
2025-07-02 05:47:29.971 bj = b[j]
2025-07-02 05:47:29.979 cruncher.set_seq2(bj)
2025-07-02 05:47:29.986 for i in range(alo, ahi):
2025-07-02 05:47:29.995 ai = a[i]
2025-07-02 05:47:30.007 if ai == bj:
2025-07-02 05:47:30.015 if eqi is None:
2025-07-02 05:47:30.022 eqi, eqj = i, j
2025-07-02 05:47:30.029 continue
2025-07-02 05:47:30.036 cruncher.set_seq1(ai)
2025-07-02 05:47:30.044 # computing similarity is expensive, so use the quick
2025-07-02 05:47:30.052 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:30.059 # compares by a factor of 3.
2025-07-02 05:47:30.067 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:30.074 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:30.081 # of the computation is cached by cruncher
2025-07-02 05:47:30.091 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:30.100 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:30.105 cruncher.ratio() > best_ratio:
2025-07-02 05:47:30.110 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:30.116 if best_ratio < cutoff:
2025-07-02 05:47:30.121 # no non-identical "pretty close" pair
2025-07-02 05:47:30.125 if eqi is None:
2025-07-02 05:47:30.130 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:30.135 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:30.140 return
2025-07-02 05:47:30.144 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:30.149 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:30.154 else:
2025-07-02 05:47:30.160 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:30.165 eqi = None
2025-07-02 05:47:30.169
2025-07-02 05:47:30.174 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:30.186 # identical
2025-07-02 05:47:30.196
2025-07-02 05:47:30.204 # pump out diffs from before the synch point
2025-07-02 05:47:30.210 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:30.215
2025-07-02 05:47:30.227 # do intraline marking on the synch pair
2025-07-02 05:47:30.236 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:30.243 if eqi is None:
2025-07-02 05:47:30.250 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:30.257 atags = btags = ""
2025-07-02 05:47:30.264 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:30.272 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:30.280 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:30.287 if tag == 'replace':
2025-07-02 05:47:30.294 atags += '^' * la
2025-07-02 05:47:30.300 btags += '^' * lb
2025-07-02 05:47:30.306 elif tag == 'delete':
2025-07-02 05:47:30.311 atags += '-' * la
2025-07-02 05:47:30.317 elif tag == 'insert':
2025-07-02 05:47:30.322 btags += '+' * lb
2025-07-02 05:47:30.328 elif tag == 'equal':
2025-07-02 05:47:30.334 atags += ' ' * la
2025-07-02 05:47:30.342 btags += ' ' * lb
2025-07-02 05:47:30.348 else:
2025-07-02 05:47:30.355 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:30.361 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:30.367 else:
2025-07-02 05:47:30.375 # the synch pair is identical
2025-07-02 05:47:30.383 yield '  ' + aelt
2025-07-02 05:47:30.396
2025-07-02 05:47:30.404 # pump out diffs from after the synch point
2025-07-02 05:47:30.411 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:30.418
2025-07-02 05:47:30.425 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:30.434 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:30.445
2025-07-02 05:47:30.454 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:30.461 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:30.466 alo = 131, ahi = 1101
2025-07-02 05:47:30.473 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:30.478 blo = 131, bhi = 1101
2025-07-02 05:47:30.483
2025-07-02 05:47:30.489 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:30.496 g = []
2025-07-02 05:47:30.502 if alo < ahi:
2025-07-02 05:47:30.509 if blo < bhi:
2025-07-02 05:47:30.516 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:30.529 else:
2025-07-02 05:47:30.541 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:30.550 elif blo < bhi:
2025-07-02 05:47:30.558 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:30.565
2025-07-02 05:47:30.571 >       yield from g
2025-07-02 05:47:30.577
2025-07-02 05:47:30.584 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:30.590 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:30.597
2025-07-02 05:47:30.609 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:30.621 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:30.630 alo = 131, ahi = 1101
2025-07-02 05:47:30.638 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:30.644 blo = 131, bhi = 1101
2025-07-02 05:47:30.651
2025-07-02 05:47:30.658 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:30.667 r"""
2025-07-02 05:47:30.676 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:30.685 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:30.697 synch point, and intraline difference marking is done on the
2025-07-02 05:47:30.707 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:30.716
2025-07-02 05:47:30.724 Example:
2025-07-02 05:47:30.732
2025-07-02 05:47:30.739 >>> d = Differ()
2025-07-02 05:47:30.747 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:30.755 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:30.763 >>> print(''.join(results), end="")
2025-07-02 05:47:30.772 - abcDefghiJkl
2025-07-02 05:47:30.788 + abcdefGhijkl
2025-07-02 05:47:30.804 """
2025-07-02 05:47:30.810
2025-07-02 05:47:30.820 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:30.832 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:30.844 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:30.850 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:30.861 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:30.872
2025-07-02 05:47:30.881 # search for the pair that matches best without being identical
2025-07-02 05:47:30.889 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:30.896 # on junk -- unless we have to)
2025-07-02 05:47:30.903 for j in range(blo, bhi):
2025-07-02 05:47:30.910 bj = b[j]
2025-07-02 05:47:30.919 cruncher.set_seq2(bj)
2025-07-02 05:47:30.925 for i in range(alo, ahi):
2025-07-02 05:47:30.931 ai = a[i]
2025-07-02 05:47:30.936 if ai == bj:
2025-07-02 05:47:30.942 if eqi is None:
2025-07-02 05:47:30.949 eqi, eqj = i, j
2025-07-02 05:47:30.955 continue
2025-07-02 05:47:30.963 cruncher.set_seq1(ai)
2025-07-02 05:47:30.973 # computing similarity is expensive, so use the quick
2025-07-02 05:47:30.986 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:30.999 # compares by a factor of 3.
2025-07-02 05:47:31.012 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:31.026 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:31.037 # of the computation is cached by cruncher
2025-07-02 05:47:31.050 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:31.059 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:31.073 cruncher.ratio() > best_ratio:
2025-07-02 05:47:31.082 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:31.095 if best_ratio < cutoff:
2025-07-02 05:47:31.107 # no non-identical "pretty close" pair
2025-07-02 05:47:31.117 if eqi is None:
2025-07-02 05:47:31.126 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:31.132 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:31.139 return
2025-07-02 05:47:31.146 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:31.152 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:31.167 else:
2025-07-02 05:47:31.179 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:31.188 eqi = None
2025-07-02 05:47:31.195
2025-07-02 05:47:31.202 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:31.211 # identical
2025-07-02 05:47:31.220
2025-07-02 05:47:31.228 # pump out diffs from before the synch point
2025-07-02 05:47:31.235 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:31.241
2025-07-02 05:47:31.247 # do intraline marking on the synch pair
2025-07-02 05:47:31.261 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:31.269 if eqi is None:
2025-07-02 05:47:31.279 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:31.289 atags = btags = ""
2025-07-02 05:47:31.297 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:31.307 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:31.319 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:31.332 if tag == 'replace':
2025-07-02 05:47:31.341 atags += '^' * la
2025-07-02 05:47:31.349 btags += '^' * lb
2025-07-02 05:47:31.355 elif tag == 'delete':
2025-07-02 05:47:31.361 atags += '-' * la
2025-07-02 05:47:31.368 elif tag == 'insert':
2025-07-02 05:47:31.374 btags += '+' * lb
2025-07-02 05:47:31.381 elif tag == 'equal':
2025-07-02 05:47:31.391 atags += ' ' * la
2025-07-02 05:47:31.407 btags += ' ' * lb
2025-07-02 05:47:31.417 else:
2025-07-02 05:47:31.430 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:31.439 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:31.447 else:
2025-07-02 05:47:31.456 # the synch pair is identical
2025-07-02 05:47:31.465 yield '  ' + aelt
2025-07-02 05:47:31.474
2025-07-02 05:47:31.485 # pump out diffs from after the synch point
2025-07-02 05:47:31.493 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:31.500
2025-07-02 05:47:31.508 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:31.521 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:31.531
2025-07-02 05:47:31.541 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:31.550 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:31.561 alo = 132, ahi = 1101
2025-07-02 05:47:31.573 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:31.582 blo = 132, bhi = 1101
2025-07-02 05:47:31.594
2025-07-02 05:47:31.605 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:31.612 g = []
2025-07-02 05:47:31.623 if alo < ahi:
2025-07-02 05:47:31.635 if blo < bhi:
2025-07-02 05:47:31.647 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:31.658 else:
2025-07-02 05:47:31.671 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:31.682 elif blo < bhi:
2025-07-02 05:47:31.693 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:31.704
2025-07-02 05:47:31.713 >       yield from g
2025-07-02 05:47:31.720
2025-07-02 05:47:31.732 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:31.745 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:31.758
2025-07-02 05:47:31.772 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:31.785 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:31.798 alo = 132, ahi = 1101
2025-07-02 05:47:31.808 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:31.817 blo = 132, bhi = 1101
2025-07-02 05:47:31.823
2025-07-02 05:47:31.832 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:31.845 r"""
2025-07-02 05:47:31.856 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:31.866 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:31.877 synch point, and intraline difference marking is done on the
2025-07-02 05:47:31.888 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:31.898
2025-07-02 05:47:31.912 Example:
2025-07-02 05:47:31.924
2025-07-02 05:47:31.933 >>> d = Differ()
2025-07-02 05:47:31.946 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:31.957 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:31.969 >>> print(''.join(results), end="")
2025-07-02 05:47:31.980 - abcDefghiJkl
2025-07-02 05:47:31.996 + abcdefGhijkl
2025-07-02 05:47:32.022 """
2025-07-02 05:47:32.034
2025-07-02 05:47:32.050 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:32.063 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:32.075 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:32.084 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:32.092 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:32.100
2025-07-02 05:47:32.107 # search for the pair that matches best without being identical
2025-07-02 05:47:32.115 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:32.123 # on junk -- unless we have to)
2025-07-02 05:47:32.132 for j in range(blo, bhi):
2025-07-02 05:47:32.140 bj = b[j]
2025-07-02 05:47:32.147 cruncher.set_seq2(bj)
2025-07-02 05:47:32.156 for i in range(alo, ahi):
2025-07-02 05:47:32.166 ai = a[i]
2025-07-02 05:47:32.175 if ai == bj:
2025-07-02 05:47:32.182 if eqi is None:
2025-07-02 05:47:32.193 eqi, eqj = i, j
2025-07-02 05:47:32.202 continue
2025-07-02 05:47:32.210 cruncher.set_seq1(ai)
2025-07-02 05:47:32.222 # computing similarity is expensive, so use the quick
2025-07-02 05:47:32.232 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:32.240 # compares by a factor of 3.
2025-07-02 05:47:32.248 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:32.255 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:32.262 # of the computation is cached by cruncher
2025-07-02 05:47:32.269 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:32.274 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:32.281 cruncher.ratio() > best_ratio:
2025-07-02 05:47:32.287 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:32.296 if best_ratio < cutoff:
2025-07-02 05:47:32.307 # no non-identical "pretty close" pair
2025-07-02 05:47:32.316 if eqi is None:
2025-07-02 05:47:32.323 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:32.331 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:32.338 return
2025-07-02 05:47:32.352 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:32.362 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:32.369 else:
2025-07-02 05:47:32.376 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:32.382 eqi = None
2025-07-02 05:47:32.387
2025-07-02 05:47:32.393 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:32.398 # identical
2025-07-02 05:47:32.406
2025-07-02 05:47:32.416 # pump out diffs from before the synch point
2025-07-02 05:47:32.423 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:32.431
2025-07-02 05:47:32.442 # do intraline marking on the synch pair
2025-07-02 05:47:32.451 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:32.460 if eqi is None:
2025-07-02 05:47:32.468 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:32.478 atags = btags = ""
2025-07-02 05:47:32.490 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:32.499 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:32.508 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:32.515 if tag == 'replace':
2025-07-02 05:47:32.522 atags += '^' * la
2025-07-02 05:47:32.528 btags += '^' * lb
2025-07-02 05:47:32.534 elif tag == 'delete':
2025-07-02 05:47:32.540 atags += '-' * la
2025-07-02 05:47:32.545 elif tag == 'insert':
2025-07-02 05:47:32.550 btags += '+' * lb
2025-07-02 05:47:32.555 elif tag == 'equal':
2025-07-02 05:47:32.560 atags += ' ' * la
2025-07-02 05:47:32.565 btags += ' ' * lb
2025-07-02 05:47:32.570 else:
2025-07-02 05:47:32.575 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:32.581 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:32.586 else:
2025-07-02 05:47:32.592 # the synch pair is identical
2025-07-02 05:47:32.598 yield '  ' + aelt
2025-07-02 05:47:32.604
2025-07-02 05:47:32.610 # pump out diffs from after the synch point
2025-07-02 05:47:32.618 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:32.624
2025-07-02 05:47:32.631 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:32.638 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:32.645
2025-07-02 05:47:32.651 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:32.658 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:32.666 alo = 133, ahi = 1101
2025-07-02 05:47:32.674 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:32.682 blo = 133, bhi = 1101
2025-07-02 05:47:32.690
2025-07-02 05:47:32.696 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:32.702 g = []
2025-07-02 05:47:32.709 if alo < ahi:
2025-07-02 05:47:32.715 if blo < bhi:
2025-07-02 05:47:32.722 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:32.728 else:
2025-07-02 05:47:32.735 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:32.741 elif blo < bhi:
2025-07-02 05:47:32.748 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:32.754
2025-07-02 05:47:32.760 >       yield from g
2025-07-02 05:47:32.766
2025-07-02 05:47:32.773 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:32.782 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:32.789
2025-07-02 05:47:32.796 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:32.805 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:32.817 alo = 133, ahi = 1101
2025-07-02 05:47:32.825 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:32.833 blo = 133, bhi = 1101
2025-07-02 05:47:32.839
2025-07-02 05:47:32.846 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:32.852 r"""
2025-07-02 05:47:32.858 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:32.864 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:32.872 synch point, and intraline difference marking is done on the
2025-07-02 05:47:32.880 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:32.886
2025-07-02 05:47:32.896 Example:
2025-07-02 05:47:32.909
2025-07-02 05:47:32.917 >>> d = Differ()
2025-07-02 05:47:32.923 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:32.931 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:32.940 >>> print(''.join(results), end="")
2025-07-02 05:47:32.947 - abcDefghiJkl
2025-07-02 05:47:32.962 + abcdefGhijkl
2025-07-02 05:47:32.985 """
2025-07-02 05:47:32.995
2025-07-02 05:47:33.004 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:33.012 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:33.020 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:33.028 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:33.037 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:33.044
2025-07-02 05:47:33.052 # search for the pair that matches best without being identical
2025-07-02 05:47:33.061 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:33.069 # on junk -- unless we have to)
2025-07-02 05:47:33.076 for j in range(blo, bhi):
2025-07-02 05:47:33.084 bj = b[j]
2025-07-02 05:47:33.091 cruncher.set_seq2(bj)
2025-07-02 05:47:33.098 for i in range(alo, ahi):
2025-07-02 05:47:33.109 ai = a[i]
2025-07-02 05:47:33.117 if ai == bj:
2025-07-02 05:47:33.125 if eqi is None:
2025-07-02 05:47:33.132 eqi, eqj = i, j
2025-07-02 05:47:33.137 continue
2025-07-02 05:47:33.142 cruncher.set_seq1(ai)
2025-07-02 05:47:33.147 # computing similarity is expensive, so use the quick
2025-07-02 05:47:33.152 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:33.165 # compares by a factor of 3.
2025-07-02 05:47:33.173 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:33.180 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:33.187 # of the computation is cached by cruncher
2025-07-02 05:47:33.195 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:33.205 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:33.213 cruncher.ratio() > best_ratio:
2025-07-02 05:47:33.219 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:33.225 if best_ratio < cutoff:
2025-07-02 05:47:33.230 # no non-identical "pretty close" pair
2025-07-02 05:47:33.236 if eqi is None:
2025-07-02 05:47:33.243 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:33.249 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:33.256 return
2025-07-02 05:47:33.265 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:33.272 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:33.279 else:
2025-07-02 05:47:33.287 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:33.295 eqi = None
2025-07-02 05:47:33.304
2025-07-02 05:47:33.311 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:33.317 # identical
2025-07-02 05:47:33.324
2025-07-02 05:47:33.331 # pump out diffs from before the synch point
2025-07-02 05:47:33.339 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:33.348
2025-07-02 05:47:33.358 # do intraline marking on the synch pair
2025-07-02 05:47:33.367 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:33.376 if eqi is None:
2025-07-02 05:47:33.385 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:33.398 atags = btags = ""
2025-07-02 05:47:33.408 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:33.416 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:33.422 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:33.427 if tag == 'replace':
2025-07-02 05:47:33.431 atags += '^' * la
2025-07-02 05:47:33.436 btags += '^' * lb
2025-07-02 05:47:33.444 elif tag == 'delete':
2025-07-02 05:47:33.453 atags += '-' * la
2025-07-02 05:47:33.460 elif tag == 'insert':
2025-07-02 05:47:33.467 btags += '+' * lb
2025-07-02 05:47:33.476 elif tag == 'equal':
2025-07-02 05:47:33.490 atags += ' ' * la
2025-07-02 05:47:33.498 btags += ' ' * lb
2025-07-02 05:47:33.505 else:
2025-07-02 05:47:33.510 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:33.515 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:33.523 else:
2025-07-02 05:47:33.536 # the synch pair is identical
2025-07-02 05:47:33.544 yield '  ' + aelt
2025-07-02 05:47:33.550
2025-07-02 05:47:33.555 # pump out diffs from after the synch point
2025-07-02 05:47:33.562 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:33.568
2025-07-02 05:47:33.573 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:33.579 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:33.586
2025-07-02 05:47:33.595 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:33.602 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:33.609 alo = 136, ahi = 1101
2025-07-02 05:47:33.616 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:33.624 blo = 136, bhi = 1101
2025-07-02 05:47:33.631
2025-07-02 05:47:33.641 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:33.652 g = []
2025-07-02 05:47:33.662 if alo < ahi:
2025-07-02 05:47:33.670 if blo < bhi:
2025-07-02 05:47:33.677 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:33.686 else:
2025-07-02 05:47:33.696 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:33.703 elif blo < bhi:
2025-07-02 05:47:33.710 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:33.716
2025-07-02 05:47:33.722 >       yield from g
2025-07-02 05:47:33.728
2025-07-02 05:47:33.738 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:33.751 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:33.759
2025-07-02 05:47:33.767 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:33.775 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:33.783 alo = 136, ahi = 1101
2025-07-02 05:47:33.791 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:33.797 blo = 136, bhi = 1101
2025-07-02 05:47:33.803
2025-07-02 05:47:33.810 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:33.816 r"""
2025-07-02 05:47:33.823 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:33.831 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:33.839 synch point, and intraline difference marking is done on the
2025-07-02 05:47:33.845 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:33.851
2025-07-02 05:47:33.859 Example:
2025-07-02 05:47:33.873
2025-07-02 05:47:33.879 >>> d = Differ()
2025-07-02 05:47:33.886 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:33.894 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:33.905 >>> print(''.join(results), end="")
2025-07-02 05:47:33.914 - abcDefghiJkl
2025-07-02 05:47:33.933 + abcdefGhijkl
2025-07-02 05:47:33.953 """
2025-07-02 05:47:33.963
2025-07-02 05:47:33.971 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:33.977 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:33.983 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:33.994 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:34.007 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:34.016
2025-07-02 05:47:34.024 # search for the pair that matches best without being identical
2025-07-02 05:47:34.032 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:34.039 # on junk -- unless we have to)
2025-07-02 05:47:34.046 for j in range(blo, bhi):
2025-07-02 05:47:34.062 bj = b[j]
2025-07-02 05:47:34.072 cruncher.set_seq2(bj)
2025-07-02 05:47:34.081 for i in range(alo, ahi):
2025-07-02 05:47:34.089 ai = a[i]
2025-07-02 05:47:34.096 if ai == bj:
2025-07-02 05:47:34.103 if eqi is None:
2025-07-02 05:47:34.109 eqi, eqj = i, j
2025-07-02 05:47:34.115 continue
2025-07-02 05:47:34.122 cruncher.set_seq1(ai)
2025-07-02 05:47:34.128 # computing similarity is expensive, so use the quick
2025-07-02 05:47:34.136 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:34.142 # compares by a factor of 3.
2025-07-02 05:47:34.149 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:34.156 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:34.162 # of the computation is cached by cruncher
2025-07-02 05:47:34.168 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:34.175 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:34.182 cruncher.ratio() > best_ratio:
2025-07-02 05:47:34.190 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:34.201 if best_ratio < cutoff:
2025-07-02 05:47:34.213 # no non-identical "pretty close" pair
2025-07-02 05:47:34.222 if eqi is None:
2025-07-02 05:47:34.230 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:34.239 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:34.250 return
2025-07-02 05:47:34.259 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:34.266 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:34.276 else:
2025-07-02 05:47:34.285 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:34.295 eqi = None
2025-07-02 05:47:34.308
2025-07-02 05:47:34.318 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:34.328 # identical
2025-07-02 05:47:34.339
2025-07-02 05:47:34.349 # pump out diffs from before the synch point
2025-07-02 05:47:34.358 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:34.369
2025-07-02 05:47:34.382 # do intraline marking on the synch pair
2025-07-02 05:47:34.390 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:34.400 if eqi is None:
2025-07-02 05:47:34.409 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:34.419 atags = btags = ""
2025-07-02 05:47:34.430 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:34.440 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:34.449 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:34.456 if tag == 'replace':
2025-07-02 05:47:34.463 atags += '^' * la
2025-07-02 05:47:34.471 btags += '^' * lb
2025-07-02 05:47:34.483 elif tag == 'delete':
2025-07-02 05:47:34.491 atags += '-' * la
2025-07-02 05:47:34.501 elif tag == 'insert':
2025-07-02 05:47:34.509 btags += '+' * lb
2025-07-02 05:47:34.516 elif tag == 'equal':
2025-07-02 05:47:34.523 atags += ' ' * la
2025-07-02 05:47:34.530 btags += ' ' * lb
2025-07-02 05:47:34.539 else:
2025-07-02 05:47:34.546 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:34.553 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:34.565 else:
2025-07-02 05:47:34.573 # the synch pair is identical
2025-07-02 05:47:34.581 yield '  ' + aelt
2025-07-02 05:47:34.587
2025-07-02 05:47:34.594 # pump out diffs from after the synch point
2025-07-02 05:47:34.600 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:34.606
2025-07-02 05:47:34.612 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:34.622 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:34.633
2025-07-02 05:47:34.642 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:34.655 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:34.665 alo = 137, ahi = 1101
2025-07-02 05:47:34.677 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:34.686 blo = 137, bhi = 1101
2025-07-02 05:47:34.694
2025-07-02 05:47:34.702 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:34.709 g = []
2025-07-02 05:47:34.717 if alo < ahi:
2025-07-02 05:47:34.724 if blo < bhi:
2025-07-02 05:47:34.731 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:34.739 else:
2025-07-02 05:47:34.746 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:34.752 elif blo < bhi:
2025-07-02 05:47:34.759 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:34.765
2025-07-02 05:47:34.772 >       yield from g
2025-07-02 05:47:34.778
2025-07-02 05:47:34.784 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:34.791 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:34.797
2025-07-02 05:47:34.804 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:34.811 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:34.817 alo = 137, ahi = 1101
2025-07-02 05:47:34.827 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:34.835 blo = 137, bhi = 1101
2025-07-02 05:47:34.843
2025-07-02 05:47:34.854 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:34.863 r"""
2025-07-02 05:47:34.870 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:34.877 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:34.884 synch point, and intraline difference marking is done on the
2025-07-02 05:47:34.891 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:34.899
2025-07-02 05:47:34.908 Example:
2025-07-02 05:47:34.918
2025-07-02 05:47:34.930 >>> d = Differ()
2025-07-02 05:47:34.939 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:34.946 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:34.953 >>> print(''.join(results), end="")
2025-07-02 05:47:34.960 - abcDefghiJkl
2025-07-02 05:47:34.980 + abcdefGhijkl
2025-07-02 05:47:34.995 """
2025-07-02 05:47:35.003
2025-07-02 05:47:35.011 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:35.024 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:35.037 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:35.046 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:35.053 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:35.059
2025-07-02 05:47:35.066 # search for the pair that matches best without being identical
2025-07-02 05:47:35.075 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:35.082 # on junk -- unless we have to)
2025-07-02 05:47:35.094 for j in range(blo, bhi):
2025-07-02 05:47:35.104 bj = b[j]
2025-07-02 05:47:35.111 cruncher.set_seq2(bj)
2025-07-02 05:47:35.117 for i in range(alo, ahi):
2025-07-02 05:47:35.123 ai = a[i]
2025-07-02 05:47:35.128 if ai == bj:
2025-07-02 05:47:35.134 if eqi is None:
2025-07-02 05:47:35.140 eqi, eqj = i, j
2025-07-02 05:47:35.146 continue
2025-07-02 05:47:35.152 cruncher.set_seq1(ai)
2025-07-02 05:47:35.159 # computing similarity is expensive, so use the quick
2025-07-02 05:47:35.165 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:35.173 # compares by a factor of 3.
2025-07-02 05:47:35.180 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:35.189 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:35.196 # of the computation is cached by cruncher
2025-07-02 05:47:35.203 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:35.211 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:35.219 cruncher.ratio() > best_ratio:
2025-07-02 05:47:35.228 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:35.235 if best_ratio < cutoff:
2025-07-02 05:47:35.244 # no non-identical "pretty close" pair
2025-07-02 05:47:35.251 if eqi is None:
2025-07-02 05:47:35.259 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:35.267 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:35.275 return
2025-07-02 05:47:35.283 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:35.291 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:35.301 else:
2025-07-02 05:47:35.313 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:35.323 eqi = None
2025-07-02 05:47:35.334
2025-07-02 05:47:35.343 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:35.351 # identical
2025-07-02 05:47:35.359
2025-07-02 05:47:35.371 # pump out diffs from before the synch point
2025-07-02 05:47:35.382 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:35.390
2025-07-02 05:47:35.398 # do intraline marking on the synch pair
2025-07-02 05:47:35.410 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:35.419 if eqi is None:
2025-07-02 05:47:35.426 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:35.432 atags = btags = ""
2025-07-02 05:47:35.438 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:35.443 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:35.453 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:35.462 if tag == 'replace':
2025-07-02 05:47:35.469 atags += '^' * la
2025-07-02 05:47:35.477 btags += '^' * lb
2025-07-02 05:47:35.484 elif tag == 'delete':
2025-07-02 05:47:35.491 atags += '-' * la
2025-07-02 05:47:35.499 elif tag == 'insert':
2025-07-02 05:47:35.506 btags += '+' * lb
2025-07-02 05:47:35.513 elif tag == 'equal':
2025-07-02 05:47:35.519 atags += ' ' * la
2025-07-02 05:47:35.526 btags += ' ' * lb
2025-07-02 05:47:35.532 else:
2025-07-02 05:47:35.539 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:35.545 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:35.551 else:
2025-07-02 05:47:35.557 # the synch pair is identical
2025-07-02 05:47:35.564 yield '  ' + aelt
2025-07-02 05:47:35.574
2025-07-02 05:47:35.585 # pump out diffs from after the synch point
2025-07-02 05:47:35.595 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:35.602
2025-07-02 05:47:35.612 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:35.625 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:35.634
2025-07-02 05:47:35.641 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:35.648 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:35.655 alo = 138, ahi = 1101
2025-07-02 05:47:35.663 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:35.672 blo = 138, bhi = 1101
2025-07-02 05:47:35.680
2025-07-02 05:47:35.687 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:35.693 g = []
2025-07-02 05:47:35.701 if alo < ahi:
2025-07-02 05:47:35.708 if blo < bhi:
2025-07-02 05:47:35.714 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:35.722 else:
2025-07-02 05:47:35.727 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:35.732 elif blo < bhi:
2025-07-02 05:47:35.737 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:35.742
2025-07-02 05:47:35.747 >       yield from g
2025-07-02 05:47:35.753
2025-07-02 05:47:35.759 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:35.765 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:35.772
2025-07-02 05:47:35.778 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:35.786 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:35.795 alo = 138, ahi = 1101
2025-07-02 05:47:35.805 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:35.815 blo = 138, bhi = 1101
2025-07-02 05:47:35.823
2025-07-02 05:47:35.837 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:35.846 r"""
2025-07-02 05:47:35.854 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:35.862 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:35.869 synch point, and intraline difference marking is done on the
2025-07-02 05:47:35.875 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:35.882
2025-07-02 05:47:35.889 Example:
2025-07-02 05:47:35.901
2025-07-02 05:47:35.911 >>> d = Differ()
2025-07-02 05:47:35.918 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:35.925 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:35.930 >>> print(''.join(results), end="")
2025-07-02 05:47:35.935 - abcDefghiJkl
2025-07-02 05:47:35.945 + abcdefGhijkl
2025-07-02 05:47:35.955 """
2025-07-02 05:47:35.961
2025-07-02 05:47:35.967 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:35.974 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:35.981 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:35.988 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:35.994 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:36.001
2025-07-02 05:47:36.008 # search for the pair that matches best without being identical
2025-07-02 05:47:36.015 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:36.022 # on junk -- unless we have to)
2025-07-02 05:47:36.031 for j in range(blo, bhi):
2025-07-02 05:47:36.046 bj = b[j]
2025-07-02 05:47:36.056 cruncher.set_seq2(bj)
2025-07-02 05:47:36.063 for i in range(alo, ahi):
2025-07-02 05:47:36.070 ai = a[i]
2025-07-02 05:47:36.077 if ai == bj:
2025-07-02 05:47:36.083 if eqi is None:
2025-07-02 05:47:36.089 eqi, eqj = i, j
2025-07-02 05:47:36.096 continue
2025-07-02 05:47:36.103 cruncher.set_seq1(ai)
2025-07-02 05:47:36.114 # computing similarity is expensive, so use the quick
2025-07-02 05:47:36.124 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:36.133 # compares by a factor of 3.
2025-07-02 05:47:36.140 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:36.146 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:36.151 # of the computation is cached by cruncher
2025-07-02 05:47:36.155 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:36.160 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:36.166 cruncher.ratio() > best_ratio:
2025-07-02 05:47:36.177 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:36.186 if best_ratio < cutoff:
2025-07-02 05:47:36.196 # no non-identical "pretty close" pair
2025-07-02 05:47:36.205 if eqi is None:
2025-07-02 05:47:36.212 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:36.217 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:36.224 return
2025-07-02 05:47:36.230 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:36.236 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:36.242 else:
2025-07-02 05:47:36.251 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:36.260 eqi = None
2025-07-02 05:47:36.268
2025-07-02 05:47:36.276 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:36.283 # identical
2025-07-02 05:47:36.291
2025-07-02 05:47:36.298 # pump out diffs from before the synch point
2025-07-02 05:47:36.308 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:36.320
2025-07-02 05:47:36.332 # do intraline marking on the synch pair
2025-07-02 05:47:36.345 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:36.357 if eqi is None:
2025-07-02 05:47:36.367 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:36.377 atags = btags = ""
2025-07-02 05:47:36.389 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:36.400 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:36.410 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:36.419 if tag == 'replace':
2025-07-02 05:47:36.428 atags += '^' * la
2025-07-02 05:47:36.436 btags += '^' * lb
2025-07-02 05:47:36.449 elif tag == 'delete':
2025-07-02 05:47:36.461 atags += '-' * la
2025-07-02 05:47:36.472 elif tag == 'insert':
2025-07-02 05:47:36.481 btags += '+' * lb
2025-07-02 05:47:36.495 elif tag == 'equal':
2025-07-02 05:47:36.507 atags += ' ' * la
2025-07-02 05:47:36.516 btags += ' ' * lb
2025-07-02 05:47:36.524 else:
2025-07-02 05:47:36.530 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:36.537 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:36.542 else:
2025-07-02 05:47:36.549 # the synch pair is identical
2025-07-02 05:47:36.555 yield '  ' + aelt
2025-07-02 05:47:36.562
2025-07-02 05:47:36.576 # pump out diffs from after the synch point
2025-07-02 05:47:36.585 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:36.592
2025-07-02 05:47:36.605 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:36.615 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:36.623
2025-07-02 05:47:36.631 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:36.638 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:36.644 alo = 139, ahi = 1101
2025-07-02 05:47:36.653 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:36.665 blo = 139, bhi = 1101
2025-07-02 05:47:36.681
2025-07-02 05:47:36.694 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:36.703 g = []
2025-07-02 05:47:36.710 if alo < ahi:
2025-07-02 05:47:36.717 if blo < bhi:
2025-07-02 05:47:36.723 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:36.728 else:
2025-07-02 05:47:36.733 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:36.739 elif blo < bhi:
2025-07-02 05:47:36.746 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:36.752
2025-07-02 05:47:36.758 >       yield from g
2025-07-02 05:47:36.763
2025-07-02 05:47:36.768 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:36.773 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:36.779
2025-07-02 05:47:36.788 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:36.797 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:36.803 alo = 139, ahi = 1101
2025-07-02 05:47:36.809 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:36.813 blo = 139, bhi = 1101
2025-07-02 05:47:36.822
2025-07-02 05:47:36.832 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:36.840 r"""
2025-07-02 05:47:36.848 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:36.855 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:36.869 synch point, and intraline difference marking is done on the
2025-07-02 05:47:36.882 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:36.891
2025-07-02 05:47:36.899 Example:
2025-07-02 05:47:36.906
2025-07-02 05:47:36.919 >>> d = Differ()
2025-07-02 05:47:36.930 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:36.939 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:36.947 >>> print(''.join(results), end="")
2025-07-02 05:47:36.957 - abcDefghiJkl
2025-07-02 05:47:36.978 + abcdefGhijkl
2025-07-02 05:47:37.002 """
2025-07-02 05:47:37.014
2025-07-02 05:47:37.026 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:37.036 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:37.045 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:37.052 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:37.059 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:37.065
2025-07-02 05:47:37.072 # search for the pair that matches best without being identical
2025-07-02 05:47:37.078 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:37.084 # on junk -- unless we have to)
2025-07-02 05:47:37.090 for j in range(blo, bhi):
2025-07-02 05:47:37.105 bj = b[j]
2025-07-02 05:47:37.119 cruncher.set_seq2(bj)
2025-07-02 05:47:37.126 for i in range(alo, ahi):
2025-07-02 05:47:37.132 ai = a[i]
2025-07-02 05:47:37.137 if ai == bj:
2025-07-02 05:47:37.142 if eqi is None:
2025-07-02 05:47:37.146 eqi, eqj = i, j
2025-07-02 05:47:37.152 continue
2025-07-02 05:47:37.157 cruncher.set_seq1(ai)
2025-07-02 05:47:37.163 # computing similarity is expensive, so use the quick
2025-07-02 05:47:37.171 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:37.179 # compares by a factor of 3.
2025-07-02 05:47:37.187 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:37.195 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:37.201 # of the computation is cached by cruncher
2025-07-02 05:47:37.208 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:37.215 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:37.223 cruncher.ratio() > best_ratio:
2025-07-02 05:47:37.234 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:37.243 if best_ratio < cutoff:
2025-07-02 05:47:37.249 # no non-identical "pretty close" pair
2025-07-02 05:47:37.255 if eqi is None:
2025-07-02 05:47:37.264 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:37.276 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:37.284 return
2025-07-02 05:47:37.292 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:37.301 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:37.309 else:
2025-07-02 05:47:37.316 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:37.323 eqi = None
2025-07-02 05:47:37.329
2025-07-02 05:47:37.337 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:37.346 # identical
2025-07-02 05:47:37.354
2025-07-02 05:47:37.360 # pump out diffs from before the synch point
2025-07-02 05:47:37.366 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:37.372
2025-07-02 05:47:37.384 # do intraline marking on the synch pair
2025-07-02 05:47:37.395 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:37.402 if eqi is None:
2025-07-02 05:47:37.410 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:37.417 atags = btags = ""
2025-07-02 05:47:37.424 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:37.431 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:37.438 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:37.447 if tag == 'replace':
2025-07-02 05:47:37.458 atags += '^' * la
2025-07-02 05:47:37.466 btags += '^' * lb
2025-07-02 05:47:37.473 elif tag == 'delete':
2025-07-02 05:47:37.479 atags += '-' * la
2025-07-02 05:47:37.492 elif tag == 'insert':
2025-07-02 05:47:37.504 btags += '+' * lb
2025-07-02 05:47:37.513 elif tag == 'equal':
2025-07-02 05:47:37.520 atags += ' ' * la
2025-07-02 05:47:37.526 btags += ' ' * lb
2025-07-02 05:47:37.532 else:
2025-07-02 05:47:37.538 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:37.543 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:37.548 else:
2025-07-02 05:47:37.554 # the synch pair is identical
2025-07-02 05:47:37.560 yield '  ' + aelt
2025-07-02 05:47:37.566
2025-07-02 05:47:37.572 # pump out diffs from after the synch point
2025-07-02 05:47:37.584 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:37.596
2025-07-02 05:47:37.607 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:37.616 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:37.622
2025-07-02 05:47:37.629 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:37.635 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:37.641 alo = 140, ahi = 1101
2025-07-02 05:47:37.652 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:37.659 blo = 140, bhi = 1101
2025-07-02 05:47:37.665
2025-07-02 05:47:37.670 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:37.675 g = []
2025-07-02 05:47:37.680 if alo < ahi:
2025-07-02 05:47:37.686 if blo < bhi:
2025-07-02 05:47:37.693 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:37.700 else:
2025-07-02 05:47:37.710 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:37.723 elif blo < bhi:
2025-07-02 05:47:37.731 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:37.736
2025-07-02 05:47:37.742 >       yield from g
2025-07-02 05:47:37.748
2025-07-02 05:47:37.754 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:37.761 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:37.766
2025-07-02 05:47:37.771 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:37.776 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:37.785 alo = 140, ahi = 1101
2025-07-02 05:47:37.792 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:37.803 blo = 140, bhi = 1101
2025-07-02 05:47:37.812
2025-07-02 05:47:37.820 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:37.829 r"""
2025-07-02 05:47:37.842 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:37.853 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:37.860 synch point, and intraline difference marking is done on the
2025-07-02 05:47:37.866 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:37.872
2025-07-02 05:47:37.877 Example:
2025-07-02 05:47:37.882
2025-07-02 05:47:37.887 >>> d = Differ()
2025-07-02 05:47:37.894 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:37.899 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:37.903 >>> print(''.join(results), end="")
2025-07-02 05:47:37.908 - abcDefghiJkl
2025-07-02 05:47:37.926 + abcdefGhijkl
2025-07-02 05:47:37.941 """
2025-07-02 05:47:37.946
2025-07-02 05:47:37.952 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:37.957 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:37.961 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:37.966 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:37.973 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:37.986
2025-07-02 05:47:37.996 # search for the pair that matches best without being identical
2025-07-02 05:47:38.004 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:38.011 # on junk -- unless we have to)
2025-07-02 05:47:38.017 for j in range(blo, bhi):
2025-07-02 05:47:38.023 bj = b[j]
2025-07-02 05:47:38.030 cruncher.set_seq2(bj)
2025-07-02 05:47:38.041 for i in range(alo, ahi):
2025-07-02 05:47:38.049 ai = a[i]
2025-07-02 05:47:38.055 if ai == bj:
2025-07-02 05:47:38.061 if eqi is None:
2025-07-02 05:47:38.068 eqi, eqj = i, j
2025-07-02 05:47:38.074 continue
2025-07-02 05:47:38.081 cruncher.set_seq1(ai)
2025-07-02 05:47:38.088 # computing similarity is expensive, so use the quick
2025-07-02 05:47:38.094 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:38.100 # compares by a factor of 3.
2025-07-02 05:47:38.105 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:38.110 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:38.115 # of the computation is cached by cruncher
2025-07-02 05:47:38.121 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:38.128 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:38.134 cruncher.ratio() > best_ratio:
2025-07-02 05:47:38.141 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:38.147 if best_ratio < cutoff:
2025-07-02 05:47:38.155 # no non-identical "pretty close" pair
2025-07-02 05:47:38.163 if eqi is None:
2025-07-02 05:47:38.170 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:38.176 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:38.181 return
2025-07-02 05:47:38.190 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:38.201 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:38.209 else:
2025-07-02 05:47:38.216 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:38.221 eqi = None
2025-07-02 05:47:38.227
2025-07-02 05:47:38.233 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:38.239 # identical
2025-07-02 05:47:38.246
2025-07-02 05:47:38.255 # pump out diffs from before the synch point
2025-07-02 05:47:38.263 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:38.269
2025-07-02 05:47:38.275 # do intraline marking on the synch pair
2025-07-02 05:47:38.280 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:38.285 if eqi is None:
2025-07-02 05:47:38.290 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:38.297 atags = btags = ""
2025-07-02 05:47:38.308 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:38.314 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:38.319 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:38.325 if tag == 'replace':
2025-07-02 05:47:38.330 atags += '^' * la
2025-07-02 05:47:38.335 btags += '^' * lb
2025-07-02 05:47:38.339 elif tag == 'delete':
2025-07-02 05:47:38.344 atags += '-' * la
2025-07-02 05:47:38.349 elif tag == 'insert':
2025-07-02 05:47:38.354 btags += '+' * lb
2025-07-02 05:47:38.359 elif tag == 'equal':
2025-07-02 05:47:38.367 atags += ' ' * la
2025-07-02 05:47:38.374 btags += ' ' * lb
2025-07-02 05:47:38.381 else:
2025-07-02 05:47:38.388 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:38.395 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:38.402 else:
2025-07-02 05:47:38.410 # the synch pair is identical
2025-07-02 05:47:38.417 yield '  ' + aelt
2025-07-02 05:47:38.425
2025-07-02 05:47:38.431 # pump out diffs from after the synch point
2025-07-02 05:47:38.439 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:38.446
2025-07-02 05:47:38.455 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:38.466 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:38.474
2025-07-02 05:47:38.481 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:38.487 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:38.492 alo = 141, ahi = 1101
2025-07-02 05:47:38.498 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:38.503 blo = 141, bhi = 1101
2025-07-02 05:47:38.507
2025-07-02 05:47:38.512 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:38.519 g = []
2025-07-02 05:47:38.530 if alo < ahi:
2025-07-02 05:47:38.539 if blo < bhi:
2025-07-02 05:47:38.546 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:38.558 else:
2025-07-02 05:47:38.569 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:38.579 elif blo < bhi:
2025-07-02 05:47:38.589 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:38.596
2025-07-02 05:47:38.603 >       yield from g
2025-07-02 05:47:38.611
2025-07-02 05:47:38.622 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:38.632 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:38.639
2025-07-02 05:47:38.644 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:38.649 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:38.654 alo = 141, ahi = 1101
2025-07-02 05:47:38.660 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:38.666 blo = 141, bhi = 1101
2025-07-02 05:47:38.677
2025-07-02 05:47:38.684 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:38.690 r"""
2025-07-02 05:47:38.698 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:38.709 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:38.719 synch point, and intraline difference marking is done on the
2025-07-02 05:47:38.729 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:38.741
2025-07-02 05:47:38.754 Example:
2025-07-02 05:47:38.765
2025-07-02 05:47:38.774 >>> d = Differ()
2025-07-02 05:47:38.785 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:38.793 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:38.800 >>> print(''.join(results), end="")
2025-07-02 05:47:38.806 - abcDefghiJkl
2025-07-02 05:47:38.825 + abcdefGhijkl
2025-07-02 05:47:38.843 """
2025-07-02 05:47:38.856
2025-07-02 05:47:38.866 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:38.878 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:38.889 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:38.898 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:38.907 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:38.915
2025-07-02 05:47:38.922 # search for the pair that matches best without being identical
2025-07-02 05:47:38.933 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:38.942 # on junk -- unless we have to)
2025-07-02 05:47:38.950 for j in range(blo, bhi):
2025-07-02 05:47:38.959 bj = b[j]
2025-07-02 05:47:38.969 cruncher.set_seq2(bj)
2025-07-02 05:47:38.978 for i in range(alo, ahi):
2025-07-02 05:47:38.989 ai = a[i]
2025-07-02 05:47:38.999 if ai == bj:
2025-07-02 05:47:39.006 if eqi is None:
2025-07-02 05:47:39.014 eqi, eqj = i, j
2025-07-02 05:47:39.024 continue
2025-07-02 05:47:39.034 cruncher.set_seq1(ai)
2025-07-02 05:47:39.041 # computing similarity is expensive, so use the quick
2025-07-02 05:47:39.048 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:39.054 # compares by a factor of 3.
2025-07-02 05:47:39.061 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:39.075 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:39.085 # of the computation is cached by cruncher
2025-07-02 05:47:39.097 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:39.109 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:39.122 cruncher.ratio() > best_ratio:
2025-07-02 05:47:39.135 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:39.144 if best_ratio < cutoff:
2025-07-02 05:47:39.152 # no non-identical "pretty close" pair
2025-07-02 05:47:39.159 if eqi is None:
2025-07-02 05:47:39.169 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:39.183 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:39.194 return
2025-07-02 05:47:39.203 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:39.211 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:39.219 else:
2025-07-02 05:47:39.230 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:39.238 eqi = None
2025-07-02 05:47:39.246
2025-07-02 05:47:39.254 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:39.261 # identical
2025-07-02 05:47:39.268
2025-07-02 05:47:39.279 # pump out diffs from before the synch point
2025-07-02 05:47:39.290 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:39.299
2025-07-02 05:47:39.308 # do intraline marking on the synch pair
2025-07-02 05:47:39.314 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:39.329 if eqi is None:
2025-07-02 05:47:39.338 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:39.350 atags = btags = ""
2025-07-02 05:47:39.359 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:39.368 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:39.374 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:39.385 if tag == 'replace':
2025-07-02 05:47:39.397 atags += '^' * la
2025-07-02 05:47:39.407 btags += '^' * lb
2025-07-02 05:47:39.416 elif tag == 'delete':
2025-07-02 05:47:39.424 atags += '-' * la
2025-07-02 05:47:39.432 elif tag == 'insert':
2025-07-02 05:47:39.439 btags += '+' * lb
2025-07-02 05:47:39.447 elif tag == 'equal':
2025-07-02 05:47:39.460 atags += ' ' * la
2025-07-02 05:47:39.471 btags += ' ' * lb
2025-07-02 05:47:39.480 else:
2025-07-02 05:47:39.487 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:39.493 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:39.499 else:
2025-07-02 05:47:39.505 # the synch pair is identical
2025-07-02 05:47:39.511 yield '  ' + aelt
2025-07-02 05:47:39.519
2025-07-02 05:47:39.532 # pump out diffs from after the synch point
2025-07-02 05:47:39.541 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:39.551
2025-07-02 05:47:39.563 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:39.573 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:39.581
2025-07-02 05:47:39.589 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:39.596 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:39.601 alo = 142, ahi = 1101
2025-07-02 05:47:39.607 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:39.613 blo = 142, bhi = 1101
2025-07-02 05:47:39.618
2025-07-02 05:47:39.623 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:39.629 g = []
2025-07-02 05:47:39.634 if alo < ahi:
2025-07-02 05:47:39.642 if blo < bhi:
2025-07-02 05:47:39.649 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:39.655 else:
2025-07-02 05:47:39.667 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:39.678 elif blo < bhi:
2025-07-02 05:47:39.689 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:39.699
2025-07-02 05:47:39.708 >       yield from g
2025-07-02 05:47:39.715
2025-07-02 05:47:39.724 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:39.736 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:39.747
2025-07-02 05:47:39.758 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:39.769 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:39.777 alo = 142, ahi = 1101
2025-07-02 05:47:39.786 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:39.792 blo = 142, bhi = 1101
2025-07-02 05:47:39.798
2025-07-02 05:47:39.805 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:39.811 r"""
2025-07-02 05:47:39.825 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:39.835 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:39.843 synch point, and intraline difference marking is done on the
2025-07-02 05:47:39.851 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:39.862
2025-07-02 05:47:39.874 Example:
2025-07-02 05:47:39.883
2025-07-02 05:47:39.891 >>> d = Differ()
2025-07-02 05:47:39.898 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:39.908 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:39.918 >>> print(''.join(results), end="")
2025-07-02 05:47:39.926 - abcDefghiJkl
2025-07-02 05:47:39.943 + abcdefGhijkl
2025-07-02 05:47:39.962 """
2025-07-02 05:47:39.974
2025-07-02 05:47:39.984 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:39.993 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:40.001 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:40.008 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:40.014 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:40.026
2025-07-02 05:47:40.034 # search for the pair that matches best without being identical
2025-07-02 05:47:40.042 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:40.051 # on junk -- unless we have to)
2025-07-02 05:47:40.063 for j in range(blo, bhi):
2025-07-02 05:47:40.074 bj = b[j]
2025-07-02 05:47:40.087 cruncher.set_seq2(bj)
2025-07-02 05:47:40.098 for i in range(alo, ahi):
2025-07-02 05:47:40.103 ai = a[i]
2025-07-02 05:47:40.110 if ai == bj:
2025-07-02 05:47:40.119 if eqi is None:
2025-07-02 05:47:40.128 eqi, eqj = i, j
2025-07-02 05:47:40.134 continue
2025-07-02 05:47:40.141 cruncher.set_seq1(ai)
2025-07-02 05:47:40.147 # computing similarity is expensive, so use the quick
2025-07-02 05:47:40.153 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:40.159 # compares by a factor of 3.
2025-07-02 05:47:40.166 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:40.179 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:40.189 # of the computation is cached by cruncher
2025-07-02 05:47:40.198 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:40.210 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:40.219 cruncher.ratio() > best_ratio:
2025-07-02 05:47:40.232 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:40.244 if best_ratio < cutoff:
2025-07-02 05:47:40.256 # no non-identical "pretty close" pair
2025-07-02 05:47:40.265 if eqi is None:
2025-07-02 05:47:40.278 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:40.287 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:40.295 return
2025-07-02 05:47:40.302 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:40.308 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:40.315 else:
2025-07-02 05:47:40.323 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:40.330 eqi = None
2025-07-02 05:47:40.335
2025-07-02 05:47:40.342 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:40.347 # identical
2025-07-02 05:47:40.352
2025-07-02 05:47:40.358 # pump out diffs from before the synch point
2025-07-02 05:47:40.369 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:40.381
2025-07-02 05:47:40.390 # do intraline marking on the synch pair
2025-07-02 05:47:40.403 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:40.415 if eqi is None:
2025-07-02 05:47:40.428 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:40.437 atags = btags = ""
2025-07-02 05:47:40.449 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:40.459 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:40.467 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:40.474 if tag == 'replace':
2025-07-02 05:47:40.481 atags += '^' * la
2025-07-02 05:47:40.487 btags += '^' * lb
2025-07-02 05:47:40.493 elif tag == 'delete':
2025-07-02 05:47:40.503 atags += '-' * la
2025-07-02 05:47:40.513 elif tag == 'insert':
2025-07-02 05:47:40.521 btags += '+' * lb
2025-07-02 05:47:40.528 elif tag == 'equal':
2025-07-02 05:47:40.535 atags += ' ' * la
2025-07-02 05:47:40.541 btags += ' ' * lb
2025-07-02 05:47:40.547 else:
2025-07-02 05:47:40.555 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:40.566 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:40.575 else:
2025-07-02 05:47:40.582 # the synch pair is identical
2025-07-02 05:47:40.594 yield '  ' + aelt
2025-07-02 05:47:40.604
2025-07-02 05:47:40.616 # pump out diffs from after the synch point
2025-07-02 05:47:40.627 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:40.641
2025-07-02 05:47:40.654 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:40.665 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:40.673
2025-07-02 05:47:40.681 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:40.695 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:40.707 alo = 143, ahi = 1101
2025-07-02 05:47:40.716 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:40.723 blo = 143, bhi = 1101
2025-07-02 05:47:40.730
2025-07-02 05:47:40.739 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:40.750 g = []
2025-07-02 05:47:40.763 if alo < ahi:
2025-07-02 05:47:40.774 if blo < bhi:
2025-07-02 05:47:40.783 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:40.793 else:
2025-07-02 05:47:40.807 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:40.818 elif blo < bhi:
2025-07-02 05:47:40.827 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:40.841
2025-07-02 05:47:40.851 >       yield from g
2025-07-02 05:47:40.865
2025-07-02 05:47:40.877 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:40.890 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:40.900
2025-07-02 05:47:40.910 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:40.920 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:40.927 alo = 143, ahi = 1101
2025-07-02 05:47:40.935 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:40.942 blo = 143, bhi = 1101
2025-07-02 05:47:40.948
2025-07-02 05:47:40.955 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:40.968 r"""
2025-07-02 05:47:40.977 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:40.985 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:40.995 synch point, and intraline difference marking is done on the
2025-07-02 05:47:41.009 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:41.017
2025-07-02 05:47:41.023 Example:
2025-07-02 05:47:41.029
2025-07-02 05:47:41.034 >>> d = Differ()
2025-07-02 05:47:41.041 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:41.054 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:41.065 >>> print(''.join(results), end="")
2025-07-02 05:47:41.075 - abcDefghiJkl
2025-07-02 05:47:41.093 + abcdefGhijkl
2025-07-02 05:47:41.104 """
2025-07-02 05:47:41.109
2025-07-02 05:47:41.115 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:41.123 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:41.129 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:41.134 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:41.143 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:41.154
2025-07-02 05:47:41.162 # search for the pair that matches best without being identical
2025-07-02 05:47:41.169 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:41.175 # on junk -- unless we have to)
2025-07-02 05:47:41.182 for j in range(blo, bhi):
2025-07-02 05:47:41.193 bj = b[j]
2025-07-02 05:47:41.205 cruncher.set_seq2(bj)
2025-07-02 05:47:41.214 for i in range(alo, ahi):
2025-07-02 05:47:41.221 ai = a[i]
2025-07-02 05:47:41.227 if ai == bj:
2025-07-02 05:47:41.235 if eqi is None:
2025-07-02 05:47:41.241 eqi, eqj = i, j
2025-07-02 05:47:41.248 continue
2025-07-02 05:47:41.257 cruncher.set_seq1(ai)
2025-07-02 05:47:41.269 # computing similarity is expensive, so use the quick
2025-07-02 05:47:41.282 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:41.292 # compares by a factor of 3.
2025-07-02 05:47:41.300 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:41.308 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:41.315 # of the computation is cached by cruncher
2025-07-02 05:47:41.323 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:41.336 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:41.345 cruncher.ratio() > best_ratio:
2025-07-02 05:47:41.353 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:41.360 if best_ratio < cutoff:
2025-07-02 05:47:41.371 # no non-identical "pretty close" pair
2025-07-02 05:47:41.382 if eqi is None:
2025-07-02 05:47:41.395 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:41.406 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:41.419 return
2025-07-02 05:47:41.431 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:41.442 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:41.455 else:
2025-07-02 05:47:41.466 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:41.478 eqi = None
2025-07-02 05:47:41.489
2025-07-02 05:47:41.498 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:41.504 # identical
2025-07-02 05:47:41.510
2025-07-02 05:47:41.516 # pump out diffs from before the synch point
2025-07-02 05:47:41.522 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:41.531
2025-07-02 05:47:41.540 # do intraline marking on the synch pair
2025-07-02 05:47:41.549 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:41.556 if eqi is None:
2025-07-02 05:47:41.562 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:41.568 atags = btags = ""
2025-07-02 05:47:41.573 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:41.579 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:41.586 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:41.592 if tag == 'replace':
2025-07-02 05:47:41.598 atags += '^' * la
2025-07-02 05:47:41.606 btags += '^' * lb
2025-07-02 05:47:41.613 elif tag == 'delete':
2025-07-02 05:47:41.620 atags += '-' * la
2025-07-02 05:47:41.627 elif tag == 'insert':
2025-07-02 05:47:41.634 btags += '+' * lb
2025-07-02 05:47:41.643 elif tag == 'equal':
2025-07-02 05:47:41.650 atags += ' ' * la
2025-07-02 05:47:41.659 btags += ' ' * lb
2025-07-02 05:47:41.670 else:
2025-07-02 05:47:41.679 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:41.687 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:41.693 else:
2025-07-02 05:47:41.702 # the synch pair is identical
2025-07-02 05:47:41.709 yield '  ' + aelt
2025-07-02 05:47:41.715
2025-07-02 05:47:41.723 # pump out diffs from after the synch point
2025-07-02 05:47:41.731 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:41.738
2025-07-02 05:47:41.749 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:41.759 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:41.768
2025-07-02 05:47:41.775 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:41.785 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:41.792 alo = 144, ahi = 1101
2025-07-02 05:47:41.801 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:41.808 blo = 144, bhi = 1101
2025-07-02 05:47:41.815
2025-07-02 05:47:41.823 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:41.836 g = []
2025-07-02 05:47:41.851 if alo < ahi:
2025-07-02 05:47:41.860 if blo < bhi:
2025-07-02 05:47:41.867 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:41.875 else:
2025-07-02 05:47:41.886 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:41.896 elif blo < bhi:
2025-07-02 05:47:41.904 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:41.911
2025-07-02 05:47:41.920 >       yield from g
2025-07-02 05:47:41.930
2025-07-02 05:47:41.940 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:41.951 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:41.961
2025-07-02 05:47:41.969 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:41.978 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:41.988 alo = 144, ahi = 1101
2025-07-02 05:47:41.997 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:42.004 blo = 144, bhi = 1101
2025-07-02 05:47:42.012
2025-07-02 05:47:42.018 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:42.025 r"""
2025-07-02 05:47:42.031 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:42.038 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:42.045 synch point, and intraline difference marking is done on the
2025-07-02 05:47:42.052 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:42.058
2025-07-02 05:47:42.065 Example:
2025-07-02 05:47:42.072
2025-07-02 05:47:42.078 >>> d = Differ()
2025-07-02 05:47:42.086 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:42.092 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:42.099 >>> print(''.join(results), end="")
2025-07-02 05:47:42.105 - abcDefghiJkl
2025-07-02 05:47:42.120 + abcdefGhijkl
2025-07-02 05:47:42.146 """
2025-07-02 05:47:42.159
2025-07-02 05:47:42.170 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:42.181 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:42.189 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:42.197 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:42.207 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:42.216
2025-07-02 05:47:42.227 # search for the pair that matches best without being identical
2025-07-02 05:47:42.235 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:42.244 # on junk -- unless we have to)
2025-07-02 05:47:42.256 for j in range(blo, bhi):
2025-07-02 05:47:42.267 bj = b[j]
2025-07-02 05:47:42.276 cruncher.set_seq2(bj)
2025-07-02 05:47:42.283 for i in range(alo, ahi):
2025-07-02 05:47:42.295 ai = a[i]
2025-07-02 05:47:42.302 if ai == bj:
2025-07-02 05:47:42.310 if eqi is None:
2025-07-02 05:47:42.317 eqi, eqj = i, j
2025-07-02 05:47:42.331 continue
2025-07-02 05:47:42.341 cruncher.set_seq1(ai)
2025-07-02 05:47:42.348 # computing similarity is expensive, so use the quick
2025-07-02 05:47:42.355 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:42.361 # compares by a factor of 3.
2025-07-02 05:47:42.368 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:42.375 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:42.387 # of the computation is cached by cruncher
2025-07-02 05:47:42.396 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:42.403 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:42.411 cruncher.ratio() > best_ratio:
2025-07-02 05:47:42.418 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:42.429 if best_ratio < cutoff:
2025-07-02 05:47:42.439 # no non-identical "pretty close" pair
2025-07-02 05:47:42.447 if eqi is None:
2025-07-02 05:47:42.461 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:42.476 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:42.484 return
2025-07-02 05:47:42.491 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:42.498 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:42.502 else:
2025-07-02 05:47:42.507 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:42.513 eqi = None
2025-07-02 05:47:42.519
2025-07-02 05:47:42.526 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:42.535 # identical
2025-07-02 05:47:42.542
2025-07-02 05:47:42.551 # pump out diffs from before the synch point
2025-07-02 05:47:42.560 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:42.571
2025-07-02 05:47:42.583 # do intraline marking on the synch pair
2025-07-02 05:47:42.591 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:42.599 if eqi is None:
2025-07-02 05:47:42.609 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:42.618 atags = btags = ""
2025-07-02 05:47:42.625 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:42.631 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:42.638 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:42.644 if tag == 'replace':
2025-07-02 05:47:42.650 atags += '^' * la
2025-07-02 05:47:42.661 btags += '^' * lb
2025-07-02 05:47:42.671 elif tag == 'delete':
2025-07-02 05:47:42.681 atags += '-' * la
2025-07-02 05:47:42.688 elif tag == 'insert':
2025-07-02 05:47:42.697 btags += '+' * lb
2025-07-02 05:47:42.711 elif tag == 'equal':
2025-07-02 05:47:42.723 atags += ' ' * la
2025-07-02 05:47:42.737 btags += ' ' * lb
2025-07-02 05:47:42.750 else:
2025-07-02 05:47:42.760 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:42.769 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:42.776 else:
2025-07-02 05:47:42.783 # the synch pair is identical
2025-07-02 05:47:42.795 yield '  ' + aelt
2025-07-02 05:47:42.806
2025-07-02 05:47:42.816 # pump out diffs from after the synch point
2025-07-02 05:47:42.823 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:42.829
2025-07-02 05:47:42.834 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:42.844 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:42.854
2025-07-02 05:47:42.864 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:42.877 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:42.889 alo = 145, ahi = 1101
2025-07-02 05:47:42.901 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:42.906 blo = 145, bhi = 1101
2025-07-02 05:47:42.911
2025-07-02 05:47:42.916 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:42.923 g = []
2025-07-02 05:47:42.932 if alo < ahi:
2025-07-02 05:47:42.940 if blo < bhi:
2025-07-02 05:47:42.947 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:42.954 else:
2025-07-02 05:47:42.961 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:42.966 elif blo < bhi:
2025-07-02 05:47:42.971 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:42.976
2025-07-02 05:47:42.985 >       yield from g
2025-07-02 05:47:42.997
2025-07-02 05:47:43.011 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:43.022 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:43.030
2025-07-02 05:47:43.038 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:43.047 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:43.054 alo = 145, ahi = 1101
2025-07-02 05:47:43.062 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:43.071 blo = 145, bhi = 1101
2025-07-02 05:47:43.084
2025-07-02 05:47:43.094 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:43.101 r"""
2025-07-02 05:47:43.108 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:43.114 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:43.122 synch point, and intraline difference marking is done on the
2025-07-02 05:47:43.134 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:43.144
2025-07-02 05:47:43.151 Example:
2025-07-02 05:47:43.159
2025-07-02 05:47:43.169 >>> d = Differ()
2025-07-02 05:47:43.178 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:43.184 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:43.190 >>> print(''.join(results), end="")
2025-07-02 05:47:43.196 - abcDefghiJkl
2025-07-02 05:47:43.205 + abcdefGhijkl
2025-07-02 05:47:43.215 """
2025-07-02 05:47:43.219
2025-07-02 05:47:43.224 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:43.229 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:43.233 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:43.238 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:43.243 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:43.248
2025-07-02 05:47:43.252 # search for the pair that matches best without being identical
2025-07-02 05:47:43.257 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:43.262 # on junk -- unless we have to)
2025-07-02 05:47:43.266 for j in range(blo, bhi):
2025-07-02 05:47:43.271 bj = b[j]
2025-07-02 05:47:43.275 cruncher.set_seq2(bj)
2025-07-02 05:47:43.280 for i in range(alo, ahi):
2025-07-02 05:47:43.284 ai = a[i]
2025-07-02 05:47:43.289 if ai == bj:
2025-07-02 05:47:43.293 if eqi is None:
2025-07-02 05:47:43.298 eqi, eqj = i, j
2025-07-02 05:47:43.302 continue
2025-07-02 05:47:43.307 cruncher.set_seq1(ai)
2025-07-02 05:47:43.312 # computing similarity is expensive, so use the quick
2025-07-02 05:47:43.316 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:43.321 # compares by a factor of 3.
2025-07-02 05:47:43.326 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:43.331 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:43.335 # of the computation is cached by cruncher
2025-07-02 05:47:43.340 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:43.344 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:43.349 cruncher.ratio() > best_ratio:
2025-07-02 05:47:43.354 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:43.358 if best_ratio < cutoff:
2025-07-02 05:47:43.362 # no non-identical "pretty close" pair
2025-07-02 05:47:43.367 if eqi is None:
2025-07-02 05:47:43.372 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:43.379 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:43.384 return
2025-07-02 05:47:43.389 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:43.393 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:43.398 else:
2025-07-02 05:47:43.403 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:43.407 eqi = None
2025-07-02 05:47:43.412
2025-07-02 05:47:43.419 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:43.429 # identical
2025-07-02 05:47:43.438
2025-07-02 05:47:43.446 # pump out diffs from before the synch point
2025-07-02 05:47:43.452 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:43.458
2025-07-02 05:47:43.464 # do intraline marking on the synch pair
2025-07-02 05:47:43.470 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:43.480 if eqi is None:
2025-07-02 05:47:43.492 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:43.500 atags = btags = ""
2025-07-02 05:47:43.508 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:43.515 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:43.522 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:43.528 if tag == 'replace':
2025-07-02 05:47:43.533 atags += '^' * la
2025-07-02 05:47:43.539 btags += '^' * lb
2025-07-02 05:47:43.544 elif tag == 'delete':
2025-07-02 05:47:43.548 atags += '-' * la
2025-07-02 05:47:43.556 elif tag == 'insert':
2025-07-02 05:47:43.570 btags += '+' * lb
2025-07-02 05:47:43.580 elif tag == 'equal':
2025-07-02 05:47:43.593 atags += ' ' * la
2025-07-02 05:47:43.604 btags += ' ' * lb
2025-07-02 05:47:43.615 else:
2025-07-02 05:47:43.625 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:43.633 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:43.639 else:
2025-07-02 05:47:43.646 # the synch pair is identical
2025-07-02 05:47:43.652 yield '  ' + aelt
2025-07-02 05:47:43.658
2025-07-02 05:47:43.663 # pump out diffs from after the synch point
2025-07-02 05:47:43.670 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:43.676
2025-07-02 05:47:43.682 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:43.689 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:43.694
2025-07-02 05:47:43.706 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:43.716 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:43.723 alo = 146, ahi = 1101
2025-07-02 05:47:43.731 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:43.739 blo = 146, bhi = 1101
2025-07-02 05:47:43.751
2025-07-02 05:47:43.761 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:43.768 g = []
2025-07-02 05:47:43.775 if alo < ahi:
2025-07-02 05:47:43.782 if blo < bhi:
2025-07-02 05:47:43.792 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:43.802 else:
2025-07-02 05:47:43.810 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:43.820 elif blo < bhi:
2025-07-02 05:47:43.829 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:43.840
2025-07-02 05:47:43.850 >       yield from g
2025-07-02 05:47:43.857
2025-07-02 05:47:43.867 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:43.875 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:43.882
2025-07-02 05:47:43.888 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:43.900 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:43.909 alo = 146, ahi = 1101
2025-07-02 05:47:43.916 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:43.923 blo = 146, bhi = 1101
2025-07-02 05:47:43.931
2025-07-02 05:47:43.938 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:43.945 r"""
2025-07-02 05:47:43.952 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:43.959 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:43.969 synch point, and intraline difference marking is done on the
2025-07-02 05:47:43.978 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:43.985
2025-07-02 05:47:43.992 Example:
2025-07-02 05:47:44.005
2025-07-02 05:47:44.014 >>> d = Differ()
2025-07-02 05:47:44.022 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:44.030 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:44.039 >>> print(''.join(results), end="")
2025-07-02 05:47:44.050 - abcDefghiJkl
2025-07-02 05:47:44.067 + abcdefGhijkl
2025-07-02 05:47:44.082 """
2025-07-02 05:47:44.092
2025-07-02 05:47:44.106 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:44.121 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:44.133 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:44.145 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:44.155 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:44.162
2025-07-02 05:47:44.170 # search for the pair that matches best without being identical
2025-07-02 05:47:44.181 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:44.194 # on junk -- unless we have to)
2025-07-02 05:47:44.203 for j in range(blo, bhi):
2025-07-02 05:47:44.210 bj = b[j]
2025-07-02 05:47:44.217 cruncher.set_seq2(bj)
2025-07-02 05:47:44.223 for i in range(alo, ahi):
2025-07-02 05:47:44.229 ai = a[i]
2025-07-02 05:47:44.235 if ai == bj:
2025-07-02 05:47:44.240 if eqi is None:
2025-07-02 05:47:44.246 eqi, eqj = i, j
2025-07-02 05:47:44.257 continue
2025-07-02 05:47:44.268 cruncher.set_seq1(ai)
2025-07-02 05:47:44.279 # computing similarity is expensive, so use the quick
2025-07-02 05:47:44.290 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:44.299 # compares by a factor of 3.
2025-07-02 05:47:44.307 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:44.321 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:44.335 # of the computation is cached by cruncher
2025-07-02 05:47:44.343 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:44.350 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:44.356 cruncher.ratio() > best_ratio:
2025-07-02 05:47:44.363 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:44.369 if best_ratio < cutoff:
2025-07-02 05:47:44.375 # no non-identical "pretty close" pair
2025-07-02 05:47:44.381 if eqi is None:
2025-07-02 05:47:44.396 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:44.409 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:44.418 return
2025-07-02 05:47:44.428 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:44.435 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:44.442 else:
2025-07-02 05:47:44.451 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:44.463 eqi = None
2025-07-02 05:47:44.472
2025-07-02 05:47:44.483 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:44.497 # identical
2025-07-02 05:47:44.506
2025-07-02 05:47:44.516 # pump out diffs from before the synch point
2025-07-02 05:47:44.525 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:44.532
2025-07-02 05:47:44.538 # do intraline marking on the synch pair
2025-07-02 05:47:44.544 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:44.551 if eqi is None:
2025-07-02 05:47:44.557 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:44.563 atags = btags = ""
2025-07-02 05:47:44.569 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:44.575 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:44.583 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:44.591 if tag == 'replace':
2025-07-02 05:47:44.603 atags += '^' * la
2025-07-02 05:47:44.611 btags += '^' * lb
2025-07-02 05:47:44.621 elif tag == 'delete':
2025-07-02 05:47:44.629 atags += '-' * la
2025-07-02 05:47:44.636 elif tag == 'insert':
2025-07-02 05:47:44.642 btags += '+' * lb
2025-07-02 05:47:44.652 elif tag == 'equal':
2025-07-02 05:47:44.660 atags += ' ' * la
2025-07-02 05:47:44.667 btags += ' ' * lb
2025-07-02 05:47:44.674 else:
2025-07-02 05:47:44.683 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:44.690 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:44.696 else:
2025-07-02 05:47:44.702 # the synch pair is identical
2025-07-02 05:47:44.708 yield '  ' + aelt
2025-07-02 05:47:44.715
2025-07-02 05:47:44.723 # pump out diffs from after the synch point
2025-07-02 05:47:44.734 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:44.741
2025-07-02 05:47:44.747 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:44.754 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:44.759
2025-07-02 05:47:44.765 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:44.770 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:44.777 alo = 147, ahi = 1101
2025-07-02 05:47:44.785 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:44.792 blo = 147, bhi = 1101
2025-07-02 05:47:44.799
2025-07-02 05:47:44.810 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:44.819 g = []
2025-07-02 05:47:44.826 if alo < ahi:
2025-07-02 05:47:44.836 if blo < bhi:
2025-07-02 05:47:44.847 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:44.855 else:
2025-07-02 05:47:44.867 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:44.877 elif blo < bhi:
2025-07-02 05:47:44.886 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:44.892
2025-07-02 05:47:44.898 >       yield from g
2025-07-02 05:47:44.905
2025-07-02 05:47:44.917 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:44.929 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:44.938
2025-07-02 05:47:44.948 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:44.959 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:44.968 alo = 147, ahi = 1101
2025-07-02 05:47:44.976 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:44.983 blo = 147, bhi = 1101
2025-07-02 05:47:44.993
2025-07-02 05:47:45.006 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:45.016 r"""
2025-07-02 05:47:45.025 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:45.036 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:45.050 synch point, and intraline difference marking is done on the
2025-07-02 05:47:45.064 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:45.074
2025-07-02 05:47:45.083 Example:
2025-07-02 05:47:45.095
2025-07-02 05:47:45.105 >>> d = Differ()
2025-07-02 05:47:45.112 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:45.120 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:45.126 >>> print(''.join(results), end="")
2025-07-02 05:47:45.133 - abcDefghiJkl
2025-07-02 05:47:45.145 + abcdefGhijkl
2025-07-02 05:47:45.156 """
2025-07-02 05:47:45.163
2025-07-02 05:47:45.171 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:45.185 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:45.193 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:45.201 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:45.209 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:45.217
2025-07-02 05:47:45.230 # search for the pair that matches best without being identical
2025-07-02 05:47:45.239 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:45.245 # on junk -- unless we have to)
2025-07-02 05:47:45.252 for j in range(blo, bhi):
2025-07-02 05:47:45.263 bj = b[j]
2025-07-02 05:47:45.271 cruncher.set_seq2(bj)
2025-07-02 05:47:45.278 for i in range(alo, ahi):
2025-07-02 05:47:45.285 ai = a[i]
2025-07-02 05:47:45.291 if ai == bj:
2025-07-02 05:47:45.297 if eqi is None:
2025-07-02 05:47:45.303 eqi, eqj = i, j
2025-07-02 05:47:45.309 continue
2025-07-02 05:47:45.316 cruncher.set_seq1(ai)
2025-07-02 05:47:45.324 # computing similarity is expensive, so use the quick
2025-07-02 05:47:45.334 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:45.343 # compares by a factor of 3.
2025-07-02 05:47:45.352 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:45.359 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:45.365 # of the computation is cached by cruncher
2025-07-02 05:47:45.370 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:45.376 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:45.381 cruncher.ratio() > best_ratio:
2025-07-02 05:47:45.386 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:45.394 if best_ratio < cutoff:
2025-07-02 05:47:45.401 # no non-identical "pretty close" pair
2025-07-02 05:47:45.408 if eqi is None:
2025-07-02 05:47:45.414 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:45.421 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:45.428 return
2025-07-02 05:47:45.435 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:45.441 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:45.455 else:
2025-07-02 05:47:45.465 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:45.473 eqi = None
2025-07-02 05:47:45.480
2025-07-02 05:47:45.487 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:45.493 # identical
2025-07-02 05:47:45.499
2025-07-02 05:47:45.506 # pump out diffs from before the synch point
2025-07-02 05:47:45.512 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:45.517
2025-07-02 05:47:45.523 # do intraline marking on the synch pair
2025-07-02 05:47:45.530 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:45.540 if eqi is None:
2025-07-02 05:47:45.547 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:45.554 atags = btags = ""
2025-07-02 05:47:45.563 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:45.576 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:45.588 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:45.599 if tag == 'replace':
2025-07-02 05:47:45.612 atags += '^' * la
2025-07-02 05:47:45.623 btags += '^' * lb
2025-07-02 05:47:45.637 elif tag == 'delete':
2025-07-02 05:47:45.649 atags += '-' * la
2025-07-02 05:47:45.661 elif tag == 'insert':
2025-07-02 05:47:45.674 btags += '+' * lb
2025-07-02 05:47:45.683 elif tag == 'equal':
2025-07-02 05:47:45.695 atags += ' ' * la
2025-07-02 05:47:45.704 btags += ' ' * lb
2025-07-02 05:47:45.711 else:
2025-07-02 05:47:45.717 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:45.725 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:45.732 else:
2025-07-02 05:47:45.739 # the synch pair is identical
2025-07-02 05:47:45.746 yield '  ' + aelt
2025-07-02 05:47:45.755
2025-07-02 05:47:45.766 # pump out diffs from after the synch point
2025-07-02 05:47:45.775 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:45.781
2025-07-02 05:47:45.788 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:45.794 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:45.799
2025-07-02 05:47:45.805 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:45.811 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:45.817 alo = 148, ahi = 1101
2025-07-02 05:47:45.824 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:45.830 blo = 148, bhi = 1101
2025-07-02 05:47:45.836
2025-07-02 05:47:45.842 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:45.853 g = []
2025-07-02 05:47:45.862 if alo < ahi:
2025-07-02 05:47:45.869 if blo < bhi:
2025-07-02 05:47:45.876 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:45.882 else:
2025-07-02 05:47:45.889 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:45.896 elif blo < bhi:
2025-07-02 05:47:45.903 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:45.910
2025-07-02 05:47:45.917 >       yield from g
2025-07-02 05:47:45.924
2025-07-02 05:47:45.931 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:45.939 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:45.946
2025-07-02 05:47:45.956 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:45.968 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:45.978 alo = 148, ahi = 1101
2025-07-02 05:47:45.992 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:46.004 blo = 148, bhi = 1101
2025-07-02 05:47:46.015
2025-07-02 05:47:46.023 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:46.030 r"""
2025-07-02 05:47:46.039 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:46.051 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:46.060 synch point, and intraline difference marking is done on the
2025-07-02 05:47:46.069 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:46.077
2025-07-02 05:47:46.084 Example:
2025-07-02 05:47:46.091
2025-07-02 05:47:46.103 >>> d = Differ()
2025-07-02 05:47:46.117 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:46.127 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:46.140 >>> print(''.join(results), end="")
2025-07-02 05:47:46.152 - abcDefghiJkl
2025-07-02 05:47:46.165 + abcdefGhijkl
2025-07-02 05:47:46.179 """
2025-07-02 05:47:46.192
2025-07-02 05:47:46.201 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:46.208 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:46.215 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:46.222 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:46.228 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:46.235
2025-07-02 05:47:46.242 # search for the pair that matches best without being identical
2025-07-02 05:47:46.251 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:46.263 # on junk -- unless we have to)
2025-07-02 05:47:46.272 for j in range(blo, bhi):
2025-07-02 05:47:46.282 bj = b[j]
2025-07-02 05:47:46.290 cruncher.set_seq2(bj)
2025-07-02 05:47:46.299 for i in range(alo, ahi):
2025-07-02 05:47:46.309 ai = a[i]
2025-07-02 05:47:46.317 if ai == bj:
2025-07-02 05:47:46.330 if eqi is None:
2025-07-02 05:47:46.343 eqi, eqj = i, j
2025-07-02 05:47:46.355 continue
2025-07-02 05:47:46.367 cruncher.set_seq1(ai)
2025-07-02 05:47:46.380 # computing similarity is expensive, so use the quick
2025-07-02 05:47:46.389 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:46.398 # compares by a factor of 3.
2025-07-02 05:47:46.411 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:46.421 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:46.429 # of the computation is cached by cruncher
2025-07-02 05:47:46.437 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:46.444 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:46.451 cruncher.ratio() > best_ratio:
2025-07-02 05:47:46.458 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:46.469 if best_ratio < cutoff:
2025-07-02 05:47:46.475 # no non-identical "pretty close" pair
2025-07-02 05:47:46.481 if eqi is None:
2025-07-02 05:47:46.495 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:46.505 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:46.513 return
2025-07-02 05:47:46.521 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:46.526 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:46.532 else:
2025-07-02 05:47:46.538 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:46.543 eqi = None
2025-07-02 05:47:46.548
2025-07-02 05:47:46.554 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:46.559 # identical
2025-07-02 05:47:46.564
2025-07-02 05:47:46.569 # pump out diffs from before the synch point
2025-07-02 05:47:46.575 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:46.580
2025-07-02 05:47:46.585 # do intraline marking on the synch pair
2025-07-02 05:47:46.590 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:46.595 if eqi is None:
2025-07-02 05:47:46.600 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:46.606 atags = btags = ""
2025-07-02 05:47:46.611 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:46.618 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:46.628 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:46.636 if tag == 'replace':
2025-07-02 05:47:46.645 atags += '^' * la
2025-07-02 05:47:46.651 btags += '^' * lb
2025-07-02 05:47:46.657 elif tag == 'delete':
2025-07-02 05:47:46.662 atags += '-' * la
2025-07-02 05:47:46.673 elif tag == 'insert':
2025-07-02 05:47:46.688 btags += '+' * lb
2025-07-02 05:47:46.697 elif tag == 'equal':
2025-07-02 05:47:46.708 atags += ' ' * la
2025-07-02 05:47:46.716 btags += ' ' * lb
2025-07-02 05:47:46.723 else:
2025-07-02 05:47:46.730 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:46.734 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:46.741 else:
2025-07-02 05:47:46.747 # the synch pair is identical
2025-07-02 05:47:46.752 yield '  ' + aelt
2025-07-02 05:47:46.758
2025-07-02 05:47:46.765 # pump out diffs from after the synch point
2025-07-02 05:47:46.773 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:46.780
2025-07-02 05:47:46.786 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:46.791 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:46.797
2025-07-02 05:47:46.802 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:46.816 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:46.826 alo = 149, ahi = 1101
2025-07-02 05:47:46.835 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:46.843 blo = 149, bhi = 1101
2025-07-02 05:47:46.856
2025-07-02 05:47:46.864 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:46.871 g = []
2025-07-02 05:47:46.878 if alo < ahi:
2025-07-02 05:47:46.889 if blo < bhi:
2025-07-02 05:47:46.898 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:46.905 else:
2025-07-02 05:47:46.915 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:46.925 elif blo < bhi:
2025-07-02 05:47:46.937 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:46.946
2025-07-02 05:47:46.955 >       yield from g
2025-07-02 05:47:46.961
2025-07-02 05:47:46.974 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:46.986 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:46.997
2025-07-02 05:47:47.007 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:47.017 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:47.024 alo = 149, ahi = 1101
2025-07-02 05:47:47.031 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:47.039 blo = 149, bhi = 1101
2025-07-02 05:47:47.051
2025-07-02 05:47:47.063 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:47.075 r"""
2025-07-02 05:47:47.087 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:47.096 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:47.103 synch point, and intraline difference marking is done on the
2025-07-02 05:47:47.109 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:47.115
2025-07-02 05:47:47.128 Example:
2025-07-02 05:47:47.140
2025-07-02 05:47:47.151 >>> d = Differ()
2025-07-02 05:47:47.161 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:47.172 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:47.183 >>> print(''.join(results), end="")
2025-07-02 05:47:47.194 - abcDefghiJkl
2025-07-02 05:47:47.218 + abcdefGhijkl
2025-07-02 05:47:47.239 """
2025-07-02 05:47:47.247
2025-07-02 05:47:47.254 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:47.265 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:47.273 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:47.280 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:47.292 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:47.305
2025-07-02 05:47:47.313 # search for the pair that matches best without being identical
2025-07-02 05:47:47.320 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:47.327 # on junk -- unless we have to)
2025-07-02 05:47:47.335 for j in range(blo, bhi):
2025-07-02 05:47:47.344 bj = b[j]
2025-07-02 05:47:47.355 cruncher.set_seq2(bj)
2025-07-02 05:47:47.363 for i in range(alo, ahi):
2025-07-02 05:47:47.370 ai = a[i]
2025-07-02 05:47:47.379 if ai == bj:
2025-07-02 05:47:47.390 if eqi is None:
2025-07-02 05:47:47.398 eqi, eqj = i, j
2025-07-02 05:47:47.405 continue
2025-07-02 05:47:47.412 cruncher.set_seq1(ai)
2025-07-02 05:47:47.419 # computing similarity is expensive, so use the quick
2025-07-02 05:47:47.428 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:47.436 # compares by a factor of 3.
2025-07-02 05:47:47.442 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:47.453 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:47.464 # of the computation is cached by cruncher
2025-07-02 05:47:47.472 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:47.485 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:47.491 cruncher.ratio() > best_ratio:
2025-07-02 05:47:47.500 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:47.508 if best_ratio < cutoff:
2025-07-02 05:47:47.516 # no non-identical "pretty close" pair
2025-07-02 05:47:47.523 if eqi is None:
2025-07-02 05:47:47.531 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:47.538 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:47.543 return
2025-07-02 05:47:47.550 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:47.558 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:47.566 else:
2025-07-02 05:47:47.573 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:47.582 eqi = None
2025-07-02 05:47:47.595
2025-07-02 05:47:47.606 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:47.619 # identical
2025-07-02 05:47:47.632
2025-07-02 05:47:47.643 # pump out diffs from before the synch point
2025-07-02 05:47:47.653 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:47.664
2025-07-02 05:47:47.674 # do intraline marking on the synch pair
2025-07-02 05:47:47.683 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:47.690 if eqi is None:
2025-07-02 05:47:47.703 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:47.715 atags = btags = ""
2025-07-02 05:47:47.727 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:47.735 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:47.743 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:47.755 if tag == 'replace':
2025-07-02 05:47:47.764 atags += '^' * la
2025-07-02 05:47:47.772 btags += '^' * lb
2025-07-02 05:47:47.780 elif tag == 'delete':
2025-07-02 05:47:47.790 atags += '-' * la
2025-07-02 05:47:47.798 elif tag == 'insert':
2025-07-02 05:47:47.805 btags += '+' * lb
2025-07-02 05:47:47.813 elif tag == 'equal':
2025-07-02 05:47:47.829 atags += ' ' * la
2025-07-02 05:47:47.841 btags += ' ' * lb
2025-07-02 05:47:47.855 else:
2025-07-02 05:47:47.866 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:47.876 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:47.883 else:
2025-07-02 05:47:47.890 # the synch pair is identical
2025-07-02 05:47:47.895 yield '  ' + aelt
2025-07-02 05:47:47.900
2025-07-02 05:47:47.906 # pump out diffs from after the synch point
2025-07-02 05:47:47.911 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:47.916
2025-07-02 05:47:47.921 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:47.927 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:47.933
2025-07-02 05:47:47.939 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:47.947 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:47.954 alo = 150, ahi = 1101
2025-07-02 05:47:47.963 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:47.970 blo = 150, bhi = 1101
2025-07-02 05:47:47.981
2025-07-02 05:47:47.989 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:47.995 g = []
2025-07-02 05:47:48.000 if alo < ahi:
2025-07-02 05:47:48.013 if blo < bhi:
2025-07-02 05:47:48.022 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:48.029 else:
2025-07-02 05:47:48.035 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:48.041 elif blo < bhi:
2025-07-02 05:47:48.055 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:48.064
2025-07-02 05:47:48.072 >       yield from g
2025-07-02 05:47:48.079
2025-07-02 05:47:48.086 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:48.094 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:48.107
2025-07-02 05:47:48.115 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:48.122 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:48.129 alo = 150, ahi = 1101
2025-07-02 05:47:48.137 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:48.145 blo = 150, bhi = 1101
2025-07-02 05:47:48.153
2025-07-02 05:47:48.159 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:48.164 r"""
2025-07-02 05:47:48.172 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:48.181 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:48.189 synch point, and intraline difference marking is done on the
2025-07-02 05:47:48.199 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:48.210
2025-07-02 05:47:48.219 Example:
2025-07-02 05:47:48.227
2025-07-02 05:47:48.236 >>> d = Differ()
2025-07-02 05:47:48.245 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:48.255 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:48.264 >>> print(''.join(results), end="")
2025-07-02 05:47:48.271 - abcDefghiJkl
2025-07-02 05:47:48.282 + abcdefGhijkl
2025-07-02 05:47:48.291 """
2025-07-02 05:47:48.296
2025-07-02 05:47:48.301 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:48.306 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:48.311 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:48.316 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:48.323 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:48.330
2025-07-02 05:47:48.336 # search for the pair that matches best without being identical
2025-07-02 05:47:48.343 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:48.348 # on junk -- unless we have to)
2025-07-02 05:47:48.354 for j in range(blo, bhi):
2025-07-02 05:47:48.361 bj = b[j]
2025-07-02 05:47:48.368 cruncher.set_seq2(bj)
2025-07-02 05:47:48.374 for i in range(alo, ahi):
2025-07-02 05:47:48.385 ai = a[i]
2025-07-02 05:47:48.394 if ai == bj:
2025-07-02 05:47:48.401 if eqi is None:
2025-07-02 05:47:48.408 eqi, eqj = i, j
2025-07-02 05:47:48.413 continue
2025-07-02 05:47:48.418 cruncher.set_seq1(ai)
2025-07-02 05:47:48.424 # computing similarity is expensive, so use the quick
2025-07-02 05:47:48.428 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:48.433 # compares by a factor of 3.
2025-07-02 05:47:48.438 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:48.447 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:48.457 # of the computation is cached by cruncher
2025-07-02 05:47:48.466 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:48.472 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:48.479 cruncher.ratio() > best_ratio:
2025-07-02 05:47:48.487 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:48.494 if best_ratio < cutoff:
2025-07-02 05:47:48.501 # no non-identical "pretty close" pair
2025-07-02 05:47:48.508 if eqi is None:
2025-07-02 05:47:48.515 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:48.523 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:48.531 return
2025-07-02 05:47:48.538 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:48.545 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:48.551 else:
2025-07-02 05:47:48.565 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:48.578 eqi = None
2025-07-02 05:47:48.587
2025-07-02 05:47:48.595 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:48.601 # identical
2025-07-02 05:47:48.606
2025-07-02 05:47:48.617 # pump out diffs from before the synch point
2025-07-02 05:47:48.627 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:48.634
2025-07-02 05:47:48.642 # do intraline marking on the synch pair
2025-07-02 05:47:48.654 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:48.663 if eqi is None:
2025-07-02 05:47:48.674 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:48.683 atags = btags = ""
2025-07-02 05:47:48.692 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:48.699 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:48.705 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:48.710 if tag == 'replace':
2025-07-02 05:47:48.716 atags += '^' * la
2025-07-02 05:47:48.722 btags += '^' * lb
2025-07-02 05:47:48.734 elif tag == 'delete':
2025-07-02 05:47:48.743 atags += '-' * la
2025-07-02 05:47:48.750 elif tag == 'insert':
2025-07-02 05:47:48.757 btags += '+' * lb
2025-07-02 05:47:48.763 elif tag == 'equal':
2025-07-02 05:47:48.772 atags += ' ' * la
2025-07-02 05:47:48.782 btags += ' ' * lb
2025-07-02 05:47:48.790 else:
2025-07-02 05:47:48.799 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:48.807 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:48.818 else:
2025-07-02 05:47:48.831 # the synch pair is identical
2025-07-02 05:47:48.841 yield '  ' + aelt
2025-07-02 05:47:48.849
2025-07-02 05:47:48.861 # pump out diffs from after the synch point
2025-07-02 05:47:48.872 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:48.879
2025-07-02 05:47:48.885 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:48.891 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:48.897
2025-07-02 05:47:48.902 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:48.908 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:48.914 alo = 151, ahi = 1101
2025-07-02 05:47:48.920 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:48.927 blo = 151, bhi = 1101
2025-07-02 05:47:48.934
2025-07-02 05:47:48.942 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:48.951 g = []
2025-07-02 05:47:48.957 if alo < ahi:
2025-07-02 05:47:48.963 if blo < bhi:
2025-07-02 05:47:48.969 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:48.975 else:
2025-07-02 05:47:48.981 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:48.988 elif blo < bhi:
2025-07-02 05:47:48.995 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:49.003
2025-07-02 05:47:49.010 >       yield from g
2025-07-02 05:47:49.021
2025-07-02 05:47:49.030 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:49.037 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:49.044
2025-07-02 05:47:49.051 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:49.060 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:49.068 alo = 151, ahi = 1101
2025-07-02 05:47:49.076 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:49.084 blo = 151, bhi = 1101
2025-07-02 05:47:49.092
2025-07-02 05:47:49.099 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:49.106 r"""
2025-07-02 05:47:49.112 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:49.118 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:49.125 synch point, and intraline difference marking is done on the
2025-07-02 05:47:49.131 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:49.137
2025-07-02 05:47:49.143 Example:
2025-07-02 05:47:49.149
2025-07-02 05:47:49.154 >>> d = Differ()
2025-07-02 05:47:49.163 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:49.174 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:49.181 >>> print(''.join(results), end="")
2025-07-02 05:47:49.188 - abcDefghiJkl
2025-07-02 05:47:49.200 + abcdefGhijkl
2025-07-02 05:47:49.211 """
2025-07-02 05:47:49.217
2025-07-02 05:47:49.223 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:49.228 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:49.234 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:49.240 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:49.246 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:49.253
2025-07-02 05:47:49.259 # search for the pair that matches best without being identical
2025-07-02 05:47:49.267 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:49.273 # on junk -- unless we have to)
2025-07-02 05:47:49.278 for j in range(blo, bhi):
2025-07-02 05:47:49.284 bj = b[j]
2025-07-02 05:47:49.290 cruncher.set_seq2(bj)
2025-07-02 05:47:49.296 for i in range(alo, ahi):
2025-07-02 05:47:49.303 ai = a[i]
2025-07-02 05:47:49.310 if ai == bj:
2025-07-02 05:47:49.320 if eqi is None:
2025-07-02 05:47:49.330 eqi, eqj = i, j
2025-07-02 05:47:49.337 continue
2025-07-02 05:47:49.344 cruncher.set_seq1(ai)
2025-07-02 05:47:49.351 # computing similarity is expensive, so use the quick
2025-07-02 05:47:49.359 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:49.367 # compares by a factor of 3.
2025-07-02 05:47:49.375 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:49.382 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:49.390 # of the computation is cached by cruncher
2025-07-02 05:47:49.397 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:49.403 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:49.409 cruncher.ratio() > best_ratio:
2025-07-02 05:47:49.414 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:49.418 if best_ratio < cutoff:
2025-07-02 05:47:49.423 # no non-identical "pretty close" pair
2025-07-02 05:47:49.428 if eqi is None:
2025-07-02 05:47:49.433 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:49.440 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:49.446 return
2025-07-02 05:47:49.452 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:49.458 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:49.463 else:
2025-07-02 05:47:49.468 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:49.473 eqi = None
2025-07-02 05:47:49.478
2025-07-02 05:47:49.485 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:49.492 # identical
2025-07-02 05:47:49.498
2025-07-02 05:47:49.506 # pump out diffs from before the synch point
2025-07-02 05:47:49.512 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:49.519
2025-07-02 05:47:49.525 # do intraline marking on the synch pair
2025-07-02 05:47:49.531 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:49.537 if eqi is None:
2025-07-02 05:47:49.544 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:49.550 atags = btags = ""
2025-07-02 05:47:49.561 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:49.571 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:49.579 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:49.585 if tag == 'replace':
2025-07-02 05:47:49.592 atags += '^' * la
2025-07-02 05:47:49.598 btags += '^' * lb
2025-07-02 05:47:49.604 elif tag == 'delete':
2025-07-02 05:47:49.611 atags += '-' * la
2025-07-02 05:47:49.618 elif tag == 'insert':
2025-07-02 05:47:49.628 btags += '+' * lb
2025-07-02 05:47:49.637 elif tag == 'equal':
2025-07-02 05:47:49.644 atags += ' ' * la
2025-07-02 05:47:49.651 btags += ' ' * lb
2025-07-02 05:47:49.657 else:
2025-07-02 05:47:49.664 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:49.672 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:49.679 else:
2025-07-02 05:47:49.686 # the synch pair is identical
2025-07-02 05:47:49.694 yield '  ' + aelt
2025-07-02 05:47:49.703
2025-07-02 05:47:49.711 # pump out diffs from after the synch point
2025-07-02 05:47:49.716 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:49.721
2025-07-02 05:47:49.727 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:49.732 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:49.737
2025-07-02 05:47:49.743 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:49.749 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:49.753 alo = 152, ahi = 1101
2025-07-02 05:47:49.759 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:49.765 blo = 152, bhi = 1101
2025-07-02 05:47:49.771
2025-07-02 05:47:49.779 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:49.785 g = []
2025-07-02 05:47:49.792 if alo < ahi:
2025-07-02 05:47:49.798 if blo < bhi:
2025-07-02 05:47:49.805 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:49.813 else:
2025-07-02 05:47:49.821 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:49.833 elif blo < bhi:
2025-07-02 05:47:49.842 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:49.850
2025-07-02 05:47:49.856 >       yield from g
2025-07-02 05:47:49.861
2025-07-02 05:47:49.866 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:49.871 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:49.876
2025-07-02 05:47:49.881 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:49.886 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:49.891 alo = 152, ahi = 1101
2025-07-02 05:47:49.896 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:49.901 blo = 152, bhi = 1101
2025-07-02 05:47:49.906
2025-07-02 05:47:49.917 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:49.925 r"""
2025-07-02 05:47:49.931 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:49.937 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:49.942 synch point, and intraline difference marking is done on the
2025-07-02 05:47:49.948 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:49.954
2025-07-02 05:47:49.959 Example:
2025-07-02 05:47:49.965
2025-07-02 05:47:49.970 >>> d = Differ()
2025-07-02 05:47:49.977 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:49.983 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:49.995 >>> print(''.join(results), end="")
2025-07-02 05:47:50.007 - abcDefghiJkl
2025-07-02 05:47:50.032 + abcdefGhijkl
2025-07-02 05:47:50.053 """
2025-07-02 05:47:50.061
2025-07-02 05:47:50.068 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:50.079 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:50.086 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:50.093 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:50.100 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:50.106
2025-07-02 05:47:50.112 # search for the pair that matches best without being identical
2025-07-02 05:47:50.119 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:50.126 # on junk -- unless we have to)
2025-07-02 05:47:50.131 for j in range(blo, bhi):
2025-07-02 05:47:50.137 bj = b[j]
2025-07-02 05:47:50.143 cruncher.set_seq2(bj)
2025-07-02 05:47:50.151 for i in range(alo, ahi):
2025-07-02 05:47:50.162 ai = a[i]
2025-07-02 05:47:50.171 if ai == bj:
2025-07-02 05:47:50.179 if eqi is None:
2025-07-02 05:47:50.188 eqi, eqj = i, j
2025-07-02 05:47:50.203 continue
2025-07-02 05:47:50.213 cruncher.set_seq1(ai)
2025-07-02 05:47:50.221 # computing similarity is expensive, so use the quick
2025-07-02 05:47:50.233 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:50.242 # compares by a factor of 3.
2025-07-02 05:47:50.250 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:50.262 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:50.272 # of the computation is cached by cruncher
2025-07-02 05:47:50.283 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:50.293 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:50.303 cruncher.ratio() > best_ratio:
2025-07-02 05:47:50.314 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:50.325 if best_ratio < cutoff:
2025-07-02 05:47:50.337 # no non-identical "pretty close" pair
2025-07-02 05:47:50.347 if eqi is None:
2025-07-02 05:47:50.355 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:50.363 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:50.370 return
2025-07-02 05:47:50.376 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:50.386 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:50.395 else:
2025-07-02 05:47:50.402 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:50.413 eqi = None
2025-07-02 05:47:50.424
2025-07-02 05:47:50.431 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:50.437 # identical
2025-07-02 05:47:50.442
2025-07-02 05:47:50.447 # pump out diffs from before the synch point
2025-07-02 05:47:50.454 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:50.464
2025-07-02 05:47:50.475 # do intraline marking on the synch pair
2025-07-02 05:47:50.486 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:50.497 if eqi is None:
2025-07-02 05:47:50.508 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:50.521 atags = btags = ""
2025-07-02 05:47:50.532 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:50.546 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:50.558 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:50.566 if tag == 'replace':
2025-07-02 05:47:50.577 atags += '^' * la
2025-07-02 05:47:50.588 btags += '^' * lb
2025-07-02 05:47:50.597 elif tag == 'delete':
2025-07-02 05:47:50.608 atags += '-' * la
2025-07-02 05:47:50.618 elif tag == 'insert':
2025-07-02 05:47:50.628 btags += '+' * lb
2025-07-02 05:47:50.642 elif tag == 'equal':
2025-07-02 05:47:50.653 atags += ' ' * la
2025-07-02 05:47:50.666 btags += ' ' * lb
2025-07-02 05:47:50.676 else:
2025-07-02 05:47:50.684 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:50.692 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:50.698 else:
2025-07-02 05:47:50.709 # the synch pair is identical
2025-07-02 05:47:50.722 yield '  ' + aelt
2025-07-02 05:47:50.732
2025-07-02 05:47:50.743 # pump out diffs from after the synch point
2025-07-02 05:47:50.752 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:50.760
2025-07-02 05:47:50.774 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:50.786 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:50.797
2025-07-02 05:47:50.806 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:50.817 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:50.833 alo = 153, ahi = 1101
2025-07-02 05:47:50.843 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:50.851 blo = 153, bhi = 1101
2025-07-02 05:47:50.859
2025-07-02 05:47:50.870 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:50.879 g = []
2025-07-02 05:47:50.888 if alo < ahi:
2025-07-02 05:47:50.895 if blo < bhi:
2025-07-02 05:47:50.901 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:50.907 else:
2025-07-02 05:47:50.914 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:50.923 elif blo < bhi:
2025-07-02 05:47:50.929 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:50.936
2025-07-02 05:47:50.942 >       yield from g
2025-07-02 05:47:50.952
2025-07-02 05:47:50.961 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:50.970 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:50.976
2025-07-02 05:47:50.983 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:50.990 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:50.995 alo = 153, ahi = 1101
2025-07-02 05:47:51.001 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:51.006 blo = 153, bhi = 1101
2025-07-02 05:47:51.012
2025-07-02 05:47:51.018 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:51.023 r"""
2025-07-02 05:47:51.029 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:51.036 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:51.044 synch point, and intraline difference marking is done on the
2025-07-02 05:47:51.057 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:51.068
2025-07-02 05:47:51.080 Example:
2025-07-02 05:47:51.090
2025-07-02 05:47:51.097 >>> d = Differ()
2025-07-02 05:47:51.104 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:51.110 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:51.118 >>> print(''.join(results), end="")
2025-07-02 05:47:51.126 - abcDefghiJkl
2025-07-02 05:47:51.140 + abcdefGhijkl
2025-07-02 05:47:51.163 """
2025-07-02 05:47:51.173
2025-07-02 05:47:51.180 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:51.187 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:51.194 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:51.198 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:51.203 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:51.208
2025-07-02 05:47:51.213 # search for the pair that matches best without being identical
2025-07-02 05:47:51.217 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:51.222 # on junk -- unless we have to)
2025-07-02 05:47:51.227 for j in range(blo, bhi):
2025-07-02 05:47:51.232 bj = b[j]
2025-07-02 05:47:51.238 cruncher.set_seq2(bj)
2025-07-02 05:47:51.243 for i in range(alo, ahi):
2025-07-02 05:47:51.251 ai = a[i]
2025-07-02 05:47:51.261 if ai == bj:
2025-07-02 05:47:51.270 if eqi is None:
2025-07-02 05:47:51.276 eqi, eqj = i, j
2025-07-02 05:47:51.282 continue
2025-07-02 05:47:51.292 cruncher.set_seq1(ai)
2025-07-02 05:47:51.302 # computing similarity is expensive, so use the quick
2025-07-02 05:47:51.310 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:51.317 # compares by a factor of 3.
2025-07-02 05:47:51.324 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:51.330 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:51.336 # of the computation is cached by cruncher
2025-07-02 05:47:51.342 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:51.348 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:51.354 cruncher.ratio() > best_ratio:
2025-07-02 05:47:51.360 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:51.366 if best_ratio < cutoff:
2025-07-02 05:47:51.372 # no non-identical "pretty close" pair
2025-07-02 05:47:51.377 if eqi is None:
2025-07-02 05:47:51.383 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:51.391 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:51.402 return
2025-07-02 05:47:51.412 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:51.423 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:51.433 else:
2025-07-02 05:47:51.446 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:51.459 eqi = None
2025-07-02 05:47:51.470
2025-07-02 05:47:51.480 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:51.491 # identical
2025-07-02 05:47:51.500
2025-07-02 05:47:51.509 # pump out diffs from before the synch point
2025-07-02 05:47:51.516 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:51.523
2025-07-02 05:47:51.531 # do intraline marking on the synch pair
2025-07-02 05:47:51.543 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:51.551 if eqi is None:
2025-07-02 05:47:51.558 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:51.568 atags = btags = ""
2025-07-02 05:47:51.578 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:51.587 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:51.597 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:51.605 if tag == 'replace':
2025-07-02 05:47:51.616 atags += '^' * la
2025-07-02 05:47:51.628 btags += '^' * lb
2025-07-02 05:47:51.639 elif tag == 'delete':
2025-07-02 05:47:51.648 atags += '-' * la
2025-07-02 05:47:51.656 elif tag == 'insert':
2025-07-02 05:47:51.662 btags += '+' * lb
2025-07-02 05:47:51.669 elif tag == 'equal':
2025-07-02 05:47:51.675 atags += ' ' * la
2025-07-02 05:47:51.683 btags += ' ' * lb
2025-07-02 05:47:51.696 else:
2025-07-02 05:47:51.708 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:51.717 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:51.723 else:
2025-07-02 05:47:51.735 # the synch pair is identical
2025-07-02 05:47:51.744 yield '  ' + aelt
2025-07-02 05:47:51.750
2025-07-02 05:47:51.757 # pump out diffs from after the synch point
2025-07-02 05:47:51.761 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:51.765
2025-07-02 05:47:51.777 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:51.786 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:51.794
2025-07-02 05:47:51.802 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:51.810 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:51.814 alo = 154, ahi = 1101
2025-07-02 05:47:51.820 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:51.824 blo = 154, bhi = 1101
2025-07-02 05:47:51.829
2025-07-02 05:47:51.833 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:51.837 g = []
2025-07-02 05:47:51.842 if alo < ahi:
2025-07-02 05:47:51.846 if blo < bhi:
2025-07-02 05:47:51.850 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:51.855 else:
2025-07-02 05:47:51.859 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:51.863 elif blo < bhi:
2025-07-02 05:47:51.868 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:51.872
2025-07-02 05:47:51.877 >       yield from g
2025-07-02 05:47:51.881
2025-07-02 05:47:51.885 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:51.890 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:51.902
2025-07-02 05:47:51.911 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:51.919 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:51.927 alo = 154, ahi = 1101
2025-07-02 05:47:51.938 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:51.947 blo = 154, bhi = 1101
2025-07-02 05:47:51.956
2025-07-02 05:47:51.963 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:51.969 r"""
2025-07-02 05:47:51.973 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:51.978 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:51.982 synch point, and intraline difference marking is done on the
2025-07-02 05:47:51.987 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:51.991
2025-07-02 05:47:51.995 Example:
2025-07-02 05:47:52.000
2025-07-02 05:47:52.004 >>> d = Differ()
2025-07-02 05:47:52.009 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:52.013 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:52.018 >>> print(''.join(results), end="")
2025-07-02 05:47:52.022 - abcDefghiJkl
2025-07-02 05:47:52.031 + abcdefGhijkl
2025-07-02 05:47:52.040 """
2025-07-02 05:47:52.046
2025-07-02 05:47:52.053 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:52.060 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:52.066 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:52.073 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:52.079 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:52.085
2025-07-02 05:47:52.098 # search for the pair that matches best without being identical
2025-07-02 05:47:52.108 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:52.114 # on junk -- unless we have to)
2025-07-02 05:47:52.120 for j in range(blo, bhi):
2025-07-02 05:47:52.127 bj = b[j]
2025-07-02 05:47:52.133 cruncher.set_seq2(bj)
2025-07-02 05:47:52.140 for i in range(alo, ahi):
2025-07-02 05:47:52.147 ai = a[i]
2025-07-02 05:47:52.155 if ai == bj:
2025-07-02 05:47:52.166 if eqi is None:
2025-07-02 05:47:52.174 eqi, eqj = i, j
2025-07-02 05:47:52.181 continue
2025-07-02 05:47:52.187 cruncher.set_seq1(ai)
2025-07-02 05:47:52.193 # computing similarity is expensive, so use the quick
2025-07-02 05:47:52.198 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:52.203 # compares by a factor of 3.
2025-07-02 05:47:52.209 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:52.215 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:52.223 # of the computation is cached by cruncher
2025-07-02 05:47:52.232 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:52.243 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:52.252 cruncher.ratio() > best_ratio:
2025-07-02 05:47:52.261 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:52.271 if best_ratio < cutoff:
2025-07-02 05:47:52.280 # no non-identical "pretty close" pair
2025-07-02 05:47:52.292 if eqi is None:
2025-07-02 05:47:52.301 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:52.307 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:52.312 return
2025-07-02 05:47:52.317 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:52.322 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:52.326 else:
2025-07-02 05:47:52.331 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:52.336 eqi = None
2025-07-02 05:47:52.341
2025-07-02 05:47:52.346 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:52.352 # identical
2025-07-02 05:47:52.358
2025-07-02 05:47:52.365 # pump out diffs from before the synch point
2025-07-02 05:47:52.372 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:52.378
2025-07-02 05:47:52.385 # do intraline marking on the synch pair
2025-07-02 05:47:52.391 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:52.398 if eqi is None:
2025-07-02 05:47:52.404 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:52.411 atags = btags = ""
2025-07-02 05:47:52.424 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:52.441 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:52.453 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:52.460 if tag == 'replace':
2025-07-02 05:47:52.466 atags += '^' * la
2025-07-02 05:47:52.477 btags += '^' * lb
2025-07-02 05:47:52.487 elif tag == 'delete':
2025-07-02 05:47:52.497 atags += '-' * la
2025-07-02 05:47:52.504 elif tag == 'insert':
2025-07-02 05:47:52.511 btags += '+' * lb
2025-07-02 05:47:52.518 elif tag == 'equal':
2025-07-02 05:47:52.525 atags += ' ' * la
2025-07-02 05:47:52.533 btags += ' ' * lb
2025-07-02 05:47:52.539 else:
2025-07-02 05:47:52.548 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:52.560 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:52.568 else:
2025-07-02 05:47:52.576 # the synch pair is identical
2025-07-02 05:47:52.584 yield '  ' + aelt
2025-07-02 05:47:52.591
2025-07-02 05:47:52.598 # pump out diffs from after the synch point
2025-07-02 05:47:52.609 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:52.619
2025-07-02 05:47:52.627 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:52.635 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:52.641
2025-07-02 05:47:52.646 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:52.652 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:52.658 alo = 155, ahi = 1101
2025-07-02 05:47:52.666 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:52.672 blo = 155, bhi = 1101
2025-07-02 05:47:52.679
2025-07-02 05:47:52.687 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:52.694 g = []
2025-07-02 05:47:52.702 if alo < ahi:
2025-07-02 05:47:52.711 if blo < bhi:
2025-07-02 05:47:52.720 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:52.733 else:
2025-07-02 05:47:52.746 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:52.759 elif blo < bhi:
2025-07-02 05:47:52.771 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:52.781
2025-07-02 05:47:52.794 >       yield from g
2025-07-02 05:47:52.805
2025-07-02 05:47:52.818 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:52.829 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:52.837
2025-07-02 05:47:52.845 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:52.855 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:52.864 alo = 155, ahi = 1101
2025-07-02 05:47:52.880 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:52.889 blo = 155, bhi = 1101
2025-07-02 05:47:52.897
2025-07-02 05:47:52.904 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:52.919 r"""
2025-07-02 05:47:52.931 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:52.943 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:52.953 synch point, and intraline difference marking is done on the
2025-07-02 05:47:52.965 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:52.975
2025-07-02 05:47:52.985 Example:
2025-07-02 05:47:52.997
2025-07-02 05:47:53.007 >>> d = Differ()
2025-07-02 05:47:53.015 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:53.020 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:53.026 >>> print(''.join(results), end="")
2025-07-02 05:47:53.033 - abcDefghiJkl
2025-07-02 05:47:53.047 + abcdefGhijkl
2025-07-02 05:47:53.065 """
2025-07-02 05:47:53.076
2025-07-02 05:47:53.089 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:53.101 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:53.109 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:53.117 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:53.123 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:53.129
2025-07-02 05:47:53.135 # search for the pair that matches best without being identical
2025-07-02 05:47:53.141 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:53.148 # on junk -- unless we have to)
2025-07-02 05:47:53.162 for j in range(blo, bhi):
2025-07-02 05:47:53.172 bj = b[j]
2025-07-02 05:47:53.179 cruncher.set_seq2(bj)
2025-07-02 05:47:53.185 for i in range(alo, ahi):
2025-07-02 05:47:53.191 ai = a[i]
2025-07-02 05:47:53.197 if ai == bj:
2025-07-02 05:47:53.204 if eqi is None:
2025-07-02 05:47:53.211 eqi, eqj = i, j
2025-07-02 05:47:53.218 continue
2025-07-02 05:47:53.228 cruncher.set_seq1(ai)
2025-07-02 05:47:53.239 # computing similarity is expensive, so use the quick
2025-07-02 05:47:53.252 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:53.264 # compares by a factor of 3.
2025-07-02 05:47:53.277 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:53.290 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:53.301 # of the computation is cached by cruncher
2025-07-02 05:47:53.314 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:53.323 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:53.331 cruncher.ratio() > best_ratio:
2025-07-02 05:47:53.345 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:53.354 if best_ratio < cutoff:
2025-07-02 05:47:53.364 # no non-identical "pretty close" pair
2025-07-02 05:47:53.376 if eqi is None:
2025-07-02 05:47:53.386 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:53.394 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:53.405 return
2025-07-02 05:47:53.418 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:53.432 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:53.444 else:
2025-07-02 05:47:53.457 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:53.467 eqi = None
2025-07-02 05:47:53.472
2025-07-02 05:47:53.478 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:53.484 # identical
2025-07-02 05:47:53.491
2025-07-02 05:47:53.497 # pump out diffs from before the synch point
2025-07-02 05:47:53.507 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:53.517
2025-07-02 05:47:53.525 # do intraline marking on the synch pair
2025-07-02 05:47:53.531 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:53.537 if eqi is None:
2025-07-02 05:47:53.544 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:53.550 atags = btags = ""
2025-07-02 05:47:53.557 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:53.564 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:53.572 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:53.579 if tag == 'replace':
2025-07-02 05:47:53.590 atags += '^' * la
2025-07-02 05:47:53.599 btags += '^' * lb
2025-07-02 05:47:53.612 elif tag == 'delete':
2025-07-02 05:47:53.619 atags += '-' * la
2025-07-02 05:47:53.626 elif tag == 'insert':
2025-07-02 05:47:53.635 btags += '+' * lb
2025-07-02 05:47:53.647 elif tag == 'equal':
2025-07-02 05:47:53.656 atags += ' ' * la
2025-07-02 05:47:53.663 btags += ' ' * lb
2025-07-02 05:47:53.669 else:
2025-07-02 05:47:53.675 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:53.680 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:53.685 else:
2025-07-02 05:47:53.690 # the synch pair is identical
2025-07-02 05:47:53.695 yield '  ' + aelt
2025-07-02 05:47:53.701
2025-07-02 05:47:53.707 # pump out diffs from after the synch point
2025-07-02 05:47:53.713 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:53.720
2025-07-02 05:47:53.727 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:53.735 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:53.748
2025-07-02 05:47:53.759 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:53.772 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:53.782 alo = 158, ahi = 1101
2025-07-02 05:47:53.791 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:53.799 blo = 158, bhi = 1101
2025-07-02 05:47:53.804
2025-07-02 05:47:53.811 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:53.817 g = []
2025-07-02 05:47:53.822 if alo < ahi:
2025-07-02 05:47:53.833 if blo < bhi:
2025-07-02 05:47:53.844 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:53.855 else:
2025-07-02 05:47:53.865 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:53.876 elif blo < bhi:
2025-07-02 05:47:53.887 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:53.897
2025-07-02 05:47:53.905 >       yield from g
2025-07-02 05:47:53.912
2025-07-02 05:47:53.919 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:53.925 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:53.931
2025-07-02 05:47:53.938 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:53.947 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:53.959 alo = 158, ahi = 1101
2025-07-02 05:47:53.970 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:53.990 blo = 158, bhi = 1101
2025-07-02 05:47:54.001
2025-07-02 05:47:54.010 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:54.019 r"""
2025-07-02 05:47:54.026 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:54.032 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:54.037 synch point, and intraline difference marking is done on the
2025-07-02 05:47:54.042 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:54.047
2025-07-02 05:47:54.053 Example:
2025-07-02 05:47:54.063
2025-07-02 05:47:54.072 >>> d = Differ()
2025-07-02 05:47:54.082 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:54.097 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:54.107 >>> print(''.join(results), end="")
2025-07-02 05:47:54.116 - abcDefghiJkl
2025-07-02 05:47:54.138 + abcdefGhijkl
2025-07-02 05:47:54.156 """
2025-07-02 05:47:54.166
2025-07-02 05:47:54.174 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:54.183 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:54.196 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:54.208 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:54.222 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:54.232
2025-07-02 05:47:54.240 # search for the pair that matches best without being identical
2025-07-02 05:47:54.253 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:54.265 # on junk -- unless we have to)
2025-07-02 05:47:54.279 for j in range(blo, bhi):
2025-07-02 05:47:54.292 bj = b[j]
2025-07-02 05:47:54.300 cruncher.set_seq2(bj)
2025-07-02 05:47:54.307 for i in range(alo, ahi):
2025-07-02 05:47:54.315 ai = a[i]
2025-07-02 05:47:54.327 if ai == bj:
2025-07-02 05:47:54.336 if eqi is None:
2025-07-02 05:47:54.349 eqi, eqj = i, j
2025-07-02 05:47:54.360 continue
2025-07-02 05:47:54.369 cruncher.set_seq1(ai)
2025-07-02 05:47:54.383 # computing similarity is expensive, so use the quick
2025-07-02 05:47:54.394 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:54.407 # compares by a factor of 3.
2025-07-02 05:47:54.419 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:54.428 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:54.436 # of the computation is cached by cruncher
2025-07-02 05:47:54.443 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:54.450 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:54.464 cruncher.ratio() > best_ratio:
2025-07-02 05:47:54.478 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:54.487 if best_ratio < cutoff:
2025-07-02 05:47:54.495 # no non-identical "pretty close" pair
2025-07-02 05:47:54.502 if eqi is None:
2025-07-02 05:47:54.509 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:54.517 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:54.524 return
2025-07-02 05:47:54.532 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:54.539 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:54.547 else:
2025-07-02 05:47:54.554 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:54.566 eqi = None
2025-07-02 05:47:54.578
2025-07-02 05:47:54.588 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:54.596 # identical
2025-07-02 05:47:54.603
2025-07-02 05:47:54.609 # pump out diffs from before the synch point
2025-07-02 05:47:54.615 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:54.620
2025-07-02 05:47:54.634 # do intraline marking on the synch pair
2025-07-02 05:47:54.644 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:54.652 if eqi is None:
2025-07-02 05:47:54.659 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:54.665 atags = btags = ""
2025-07-02 05:47:54.673 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:54.678 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:54.685 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:54.691 if tag == 'replace':
2025-07-02 05:47:54.698 atags += '^' * la
2025-07-02 05:47:54.707 btags += '^' * lb
2025-07-02 05:47:54.719 elif tag == 'delete':
2025-07-02 05:47:54.728 atags += '-' * la
2025-07-02 05:47:54.734 elif tag == 'insert':
2025-07-02 05:47:54.740 btags += '+' * lb
2025-07-02 05:47:54.746 elif tag == 'equal':
2025-07-02 05:47:54.754 atags += ' ' * la
2025-07-02 05:47:54.762 btags += ' ' * lb
2025-07-02 05:47:54.769 else:
2025-07-02 05:47:54.776 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:54.782 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:54.789 else:
2025-07-02 05:47:54.796 # the synch pair is identical
2025-07-02 05:47:54.803 yield '  ' + aelt
2025-07-02 05:47:54.809
2025-07-02 05:47:54.816 # pump out diffs from after the synch point
2025-07-02 05:47:54.823 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:54.830
2025-07-02 05:47:54.836 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:54.843 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:54.849
2025-07-02 05:47:54.854 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:54.860 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:54.868 alo = 159, ahi = 1101
2025-07-02 05:47:54.882 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:54.892 blo = 159, bhi = 1101
2025-07-02 05:47:54.900
2025-07-02 05:47:54.908 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:54.915 g = []
2025-07-02 05:47:54.928 if alo < ahi:
2025-07-02 05:47:54.939 if blo < bhi:
2025-07-02 05:47:54.948 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:54.956 else:
2025-07-02 05:47:54.963 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:54.970 elif blo < bhi:
2025-07-02 05:47:54.979 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:54.991
2025-07-02 05:47:55.001 >       yield from g
2025-07-02 05:47:55.010
2025-07-02 05:47:55.019 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:55.028 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:55.042
2025-07-02 05:47:55.053 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:55.064 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:55.071 alo = 159, ahi = 1101
2025-07-02 05:47:55.080 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:55.086 blo = 159, bhi = 1101
2025-07-02 05:47:55.091
2025-07-02 05:47:55.097 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:55.109 r"""
2025-07-02 05:47:55.119 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:55.128 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:55.136 synch point, and intraline difference marking is done on the
2025-07-02 05:47:55.143 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:55.149
2025-07-02 05:47:55.155 Example:
2025-07-02 05:47:55.163
2025-07-02 05:47:55.176 >>> d = Differ()
2025-07-02 05:47:55.185 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:55.195 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:55.207 >>> print(''.join(results), end="")
2025-07-02 05:47:55.217 - abcDefghiJkl
2025-07-02 05:47:55.233 + abcdefGhijkl
2025-07-02 05:47:55.246 """
2025-07-02 05:47:55.257
2025-07-02 05:47:55.267 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:55.275 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:55.282 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:55.290 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:55.296 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:55.302
2025-07-02 05:47:55.309 # search for the pair that matches best without being identical
2025-07-02 05:47:55.316 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:55.323 # on junk -- unless we have to)
2025-07-02 05:47:55.337 for j in range(blo, bhi):
2025-07-02 05:47:55.346 bj = b[j]
2025-07-02 05:47:55.353 cruncher.set_seq2(bj)
2025-07-02 05:47:55.359 for i in range(alo, ahi):
2025-07-02 05:47:55.366 ai = a[i]
2025-07-02 05:47:55.379 if ai == bj:
2025-07-02 05:47:55.389 if eqi is None:
2025-07-02 05:47:55.396 eqi, eqj = i, j
2025-07-02 05:47:55.403 continue
2025-07-02 05:47:55.409 cruncher.set_seq1(ai)
2025-07-02 05:47:55.416 # computing similarity is expensive, so use the quick
2025-07-02 05:47:55.422 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:55.428 # compares by a factor of 3.
2025-07-02 05:47:55.442 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:55.452 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:55.460 # of the computation is cached by cruncher
2025-07-02 05:47:55.467 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:55.473 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:55.479 cruncher.ratio() > best_ratio:
2025-07-02 05:47:55.485 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:55.495 if best_ratio < cutoff:
2025-07-02 05:47:55.508 # no non-identical "pretty close" pair
2025-07-02 05:47:55.518 if eqi is None:
2025-07-02 05:47:55.526 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:55.535 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:55.542 return
2025-07-02 05:47:55.550 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:55.563 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:55.576 else:
2025-07-02 05:47:55.590 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:55.601 eqi = None
2025-07-02 05:47:55.609
2025-07-02 05:47:55.617 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:55.624 # identical
2025-07-02 05:47:55.630
2025-07-02 05:47:55.635 # pump out diffs from before the synch point
2025-07-02 05:47:55.641 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:55.647
2025-07-02 05:47:55.653 # do intraline marking on the synch pair
2025-07-02 05:47:55.659 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:55.667 if eqi is None:
2025-07-02 05:47:55.677 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:55.686 atags = btags = ""
2025-07-02 05:47:55.695 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:55.704 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:55.711 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:55.721 if tag == 'replace':
2025-07-02 05:47:55.734 atags += '^' * la
2025-07-02 05:47:55.747 btags += '^' * lb
2025-07-02 05:47:55.757 elif tag == 'delete':
2025-07-02 05:47:55.769 atags += '-' * la
2025-07-02 05:47:55.779 elif tag == 'insert':
2025-07-02 05:47:55.787 btags += '+' * lb
2025-07-02 05:47:55.795 elif tag == 'equal':
2025-07-02 05:47:55.803 atags += ' ' * la
2025-07-02 05:47:55.814 btags += ' ' * lb
2025-07-02 05:47:55.822 else:
2025-07-02 05:47:55.829 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:55.835 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:55.843 else:
2025-07-02 05:47:55.854 # the synch pair is identical
2025-07-02 05:47:55.863 yield '  ' + aelt
2025-07-02 05:47:55.872
2025-07-02 05:47:55.879 # pump out diffs from after the synch point
2025-07-02 05:47:55.885 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:55.892
2025-07-02 05:47:55.898 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:55.904 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:55.910
2025-07-02 05:47:55.916 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:55.925 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:55.937 alo = 160, ahi = 1101
2025-07-02 05:47:55.949 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:55.958 blo = 160, bhi = 1101
2025-07-02 05:47:55.964
2025-07-02 05:47:55.970 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:55.981 g = []
2025-07-02 05:47:55.991 if alo < ahi:
2025-07-02 05:47:55.999 if blo < bhi:
2025-07-02 05:47:56.005 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:56.010 else:
2025-07-02 05:47:56.015 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:56.020 elif blo < bhi:
2025-07-02 05:47:56.025 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:56.030
2025-07-02 05:47:56.035 >       yield from g
2025-07-02 05:47:56.043
2025-07-02 05:47:56.051 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:56.062 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:56.072
2025-07-02 05:47:56.078 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:56.085 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:56.090 alo = 160, ahi = 1101
2025-07-02 05:47:56.095 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:56.100 blo = 160, bhi = 1101
2025-07-02 05:47:56.109
2025-07-02 05:47:56.123 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:56.133 r"""
2025-07-02 05:47:56.142 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:56.150 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:56.158 synch point, and intraline difference marking is done on the
2025-07-02 05:47:56.167 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:56.175
2025-07-02 05:47:56.185 Example:
2025-07-02 05:47:56.198
2025-07-02 05:47:56.208 >>> d = Differ()
2025-07-02 05:47:56.220 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:56.232 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:56.243 >>> print(''.join(results), end="")
2025-07-02 05:47:56.256 - abcDefghiJkl
2025-07-02 05:47:56.272 + abcdefGhijkl
2025-07-02 05:47:56.287 """
2025-07-02 05:47:56.295
2025-07-02 05:47:56.301 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:56.313 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:56.324 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:56.338 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:56.348 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:56.360
2025-07-02 05:47:56.373 # search for the pair that matches best without being identical
2025-07-02 05:47:56.384 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:56.393 # on junk -- unless we have to)
2025-07-02 05:47:56.401 for j in range(blo, bhi):
2025-07-02 05:47:56.413 bj = b[j]
2025-07-02 05:47:56.424 cruncher.set_seq2(bj)
2025-07-02 05:47:56.433 for i in range(alo, ahi):
2025-07-02 05:47:56.445 ai = a[i]
2025-07-02 05:47:56.455 if ai == bj:
2025-07-02 05:47:56.463 if eqi is None:
2025-07-02 05:47:56.470 eqi, eqj = i, j
2025-07-02 05:47:56.482 continue
2025-07-02 05:47:56.494 cruncher.set_seq1(ai)
2025-07-02 05:47:56.505 # computing similarity is expensive, so use the quick
2025-07-02 05:47:56.516 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:56.525 # compares by a factor of 3.
2025-07-02 05:47:56.533 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:56.541 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:56.553 # of the computation is cached by cruncher
2025-07-02 05:47:56.560 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:56.567 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:56.576 cruncher.ratio() > best_ratio:
2025-07-02 05:47:56.586 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:56.597 if best_ratio < cutoff:
2025-07-02 05:47:56.607 # no non-identical "pretty close" pair
2025-07-02 05:47:56.619 if eqi is None:
2025-07-02 05:47:56.629 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:56.635 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:56.642 return
2025-07-02 05:47:56.655 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:56.666 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:56.676 else:
2025-07-02 05:47:56.690 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:56.701 eqi = None
2025-07-02 05:47:56.708
2025-07-02 05:47:56.718 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:56.729 # identical
2025-07-02 05:47:56.740
2025-07-02 05:47:56.751 # pump out diffs from before the synch point
2025-07-02 05:47:56.764 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:56.776
2025-07-02 05:47:56.789 # do intraline marking on the synch pair
2025-07-02 05:47:56.800 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:56.813 if eqi is None:
2025-07-02 05:47:56.830 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:56.841 atags = btags = ""
2025-07-02 05:47:56.851 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:56.858 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:56.865 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:56.870 if tag == 'replace':
2025-07-02 05:47:56.875 atags += '^' * la
2025-07-02 05:47:56.880 btags += '^' * lb
2025-07-02 05:47:56.885 elif tag == 'delete':
2025-07-02 05:47:56.890 atags += '-' * la
2025-07-02 05:47:56.895 elif tag == 'insert':
2025-07-02 05:47:56.900 btags += '+' * lb
2025-07-02 05:47:56.905 elif tag == 'equal':
2025-07-02 05:47:56.911 atags += ' ' * la
2025-07-02 05:47:56.916 btags += ' ' * lb
2025-07-02 05:47:56.924 else:
2025-07-02 05:47:56.932 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:56.940 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:56.946 else:
2025-07-02 05:47:56.952 # the synch pair is identical
2025-07-02 05:47:56.959 yield '  ' + aelt
2025-07-02 05:47:56.964
2025-07-02 05:47:56.970 # pump out diffs from after the synch point
2025-07-02 05:47:56.981 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:56.990
2025-07-02 05:47:56.998 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:57.012 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:57.023
2025-07-02 05:47:57.032 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:57.043 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:57.055 alo = 161, ahi = 1101
2025-07-02 05:47:57.065 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:57.072 blo = 161, bhi = 1101
2025-07-02 05:47:57.078
2025-07-02 05:47:57.084 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:57.089 g = []
2025-07-02 05:47:57.094 if alo < ahi:
2025-07-02 05:47:57.099 if blo < bhi:
2025-07-02 05:47:57.107 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:57.117 else:
2025-07-02 05:47:57.129 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:57.140 elif blo < bhi:
2025-07-02 05:47:57.147 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:57.153
2025-07-02 05:47:57.158 >       yield from g
2025-07-02 05:47:57.164
2025-07-02 05:47:57.174 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:57.186 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:57.196
2025-07-02 05:47:57.204 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:57.211 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:57.216 alo = 161, ahi = 1101
2025-07-02 05:47:57.222 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:57.227 blo = 161, bhi = 1101
2025-07-02 05:47:57.233
2025-07-02 05:47:57.238 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:57.244 r"""
2025-07-02 05:47:57.250 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:57.257 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:57.263 synch point, and intraline difference marking is done on the
2025-07-02 05:47:57.271 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:57.283
2025-07-02 05:47:57.293 Example:
2025-07-02 05:47:57.300
2025-07-02 05:47:57.306 >>> d = Differ()
2025-07-02 05:47:57.317 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:57.323 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:57.330 >>> print(''.join(results), end="")
2025-07-02 05:47:57.338 - abcDefghiJkl
2025-07-02 05:47:57.351 + abcdefGhijkl
2025-07-02 05:47:57.363 """
2025-07-02 05:47:57.369
2025-07-02 05:47:57.375 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:57.388 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:57.398 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:57.410 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:57.417 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:57.424
2025-07-02 05:47:57.431 # search for the pair that matches best without being identical
2025-07-02 05:47:57.438 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:57.446 # on junk -- unless we have to)
2025-07-02 05:47:57.455 for j in range(blo, bhi):
2025-07-02 05:47:57.466 bj = b[j]
2025-07-02 05:47:57.474 cruncher.set_seq2(bj)
2025-07-02 05:47:57.481 for i in range(alo, ahi):
2025-07-02 05:47:57.493 ai = a[i]
2025-07-02 05:47:57.505 if ai == bj:
2025-07-02 05:47:57.514 if eqi is None:
2025-07-02 05:47:57.521 eqi, eqj = i, j
2025-07-02 05:47:57.528 continue
2025-07-02 05:47:57.536 cruncher.set_seq1(ai)
2025-07-02 05:47:57.543 # computing similarity is expensive, so use the quick
2025-07-02 05:47:57.551 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:57.558 # compares by a factor of 3.
2025-07-02 05:47:57.564 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:57.571 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:57.577 # of the computation is cached by cruncher
2025-07-02 05:47:57.583 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:57.590 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:57.602 cruncher.ratio() > best_ratio:
2025-07-02 05:47:57.611 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:57.618 if best_ratio < cutoff:
2025-07-02 05:47:57.625 # no non-identical "pretty close" pair
2025-07-02 05:47:57.631 if eqi is None:
2025-07-02 05:47:57.638 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:57.644 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:57.649 return
2025-07-02 05:47:57.662 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:57.672 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:57.679 else:
2025-07-02 05:47:57.686 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:57.694 eqi = None
2025-07-02 05:47:57.703
2025-07-02 05:47:57.715 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:57.725 # identical
2025-07-02 05:47:57.732
2025-07-02 05:47:57.739 # pump out diffs from before the synch point
2025-07-02 05:47:57.746 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:57.753
2025-07-02 05:47:57.759 # do intraline marking on the synch pair
2025-07-02 05:47:57.765 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:57.771 if eqi is None:
2025-07-02 05:47:57.778 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:57.790 atags = btags = ""
2025-07-02 05:47:57.801 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:57.810 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:57.819 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:57.827 if tag == 'replace':
2025-07-02 05:47:57.839 atags += '^' * la
2025-07-02 05:47:57.847 btags += '^' * lb
2025-07-02 05:47:57.855 elif tag == 'delete':
2025-07-02 05:47:57.869 atags += '-' * la
2025-07-02 05:47:57.881 elif tag == 'insert':
2025-07-02 05:47:57.889 btags += '+' * lb
2025-07-02 05:47:57.896 elif tag == 'equal':
2025-07-02 05:47:57.903 atags += ' ' * la
2025-07-02 05:47:57.910 btags += ' ' * lb
2025-07-02 05:47:57.916 else:
2025-07-02 05:47:57.922 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:57.932 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:57.944 else:
2025-07-02 05:47:57.958 # the synch pair is identical
2025-07-02 05:47:57.967 yield '  ' + aelt
2025-07-02 05:47:57.974
2025-07-02 05:47:57.981 # pump out diffs from after the synch point
2025-07-02 05:47:57.987 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:57.993
2025-07-02 05:47:57.999 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:58.006 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:58.017
2025-07-02 05:47:58.028 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:58.041 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:58.053 alo = 162, ahi = 1101
2025-07-02 05:47:58.066 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:58.075 blo = 162, bhi = 1101
2025-07-02 05:47:58.083
2025-07-02 05:47:58.090 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:58.097 g = []
2025-07-02 05:47:58.103 if alo < ahi:
2025-07-02 05:47:58.111 if blo < bhi:
2025-07-02 05:47:58.121 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:58.128 else:
2025-07-02 05:47:58.135 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:58.141 elif blo < bhi:
2025-07-02 05:47:58.148 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:58.157
2025-07-02 05:47:58.165 >       yield from g
2025-07-02 05:47:58.171
2025-07-02 05:47:58.178 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:58.188 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:58.197
2025-07-02 05:47:58.207 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:58.216 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:58.227 alo = 162, ahi = 1101
2025-07-02 05:47:58.241 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:58.250 blo = 162, bhi = 1101
2025-07-02 05:47:58.259
2025-07-02 05:47:58.268 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:58.281 r"""
2025-07-02 05:47:58.291 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:58.299 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:58.307 synch point, and intraline difference marking is done on the
2025-07-02 05:47:58.316 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:58.327
2025-07-02 05:47:58.335 Example:
2025-07-02 05:47:58.344
2025-07-02 05:47:58.354 >>> d = Differ()
2025-07-02 05:47:58.361 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:58.368 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:58.375 >>> print(''.join(results), end="")
2025-07-02 05:47:58.383 - abcDefghiJkl
2025-07-02 05:47:58.401 + abcdefGhijkl
2025-07-02 05:47:58.419 """
2025-07-02 05:47:58.431
2025-07-02 05:47:58.440 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:58.447 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:58.454 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:58.464 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:58.474 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:58.484
2025-07-02 05:47:58.497 # search for the pair that matches best without being identical
2025-07-02 05:47:58.508 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:58.516 # on junk -- unless we have to)
2025-07-02 05:47:58.523 for j in range(blo, bhi):
2025-07-02 05:47:58.530 bj = b[j]
2025-07-02 05:47:58.538 cruncher.set_seq2(bj)
2025-07-02 05:47:58.548 for i in range(alo, ahi):
2025-07-02 05:47:58.556 ai = a[i]
2025-07-02 05:47:58.563 if ai == bj:
2025-07-02 05:47:58.569 if eqi is None:
2025-07-02 05:47:58.575 eqi, eqj = i, j
2025-07-02 05:47:58.581 continue
2025-07-02 05:47:58.587 cruncher.set_seq1(ai)
2025-07-02 05:47:58.596 # computing similarity is expensive, so use the quick
2025-07-02 05:47:58.607 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:58.616 # compares by a factor of 3.
2025-07-02 05:47:58.624 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:58.630 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:58.637 # of the computation is cached by cruncher
2025-07-02 05:47:58.644 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:58.651 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:58.657 cruncher.ratio() > best_ratio:
2025-07-02 05:47:58.664 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:58.674 if best_ratio < cutoff:
2025-07-02 05:47:58.683 # no non-identical "pretty close" pair
2025-07-02 05:47:58.690 if eqi is None:
2025-07-02 05:47:58.699 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:58.707 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:58.718 return
2025-07-02 05:47:58.727 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:58.733 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:58.740 else:
2025-07-02 05:47:58.746 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:58.757 eqi = None
2025-07-02 05:47:58.766
2025-07-02 05:47:58.776 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:58.790 # identical
2025-07-02 05:47:58.802
2025-07-02 05:47:58.814 # pump out diffs from before the synch point
2025-07-02 05:47:58.826 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:58.838
2025-07-02 05:47:58.849 # do intraline marking on the synch pair
2025-07-02 05:47:58.863 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:58.871 if eqi is None:
2025-07-02 05:47:58.885 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:58.898 atags = btags = ""
2025-07-02 05:47:58.909 cruncher.set_seqs(aelt, belt)
2025-07-02 05:47:58.919 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:47:58.927 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:47:58.935 if tag == 'replace':
2025-07-02 05:47:58.948 atags += '^' * la
2025-07-02 05:47:58.957 btags += '^' * lb
2025-07-02 05:47:58.968 elif tag == 'delete':
2025-07-02 05:47:58.979 atags += '-' * la
2025-07-02 05:47:58.988 elif tag == 'insert':
2025-07-02 05:47:58.995 btags += '+' * lb
2025-07-02 05:47:59.001 elif tag == 'equal':
2025-07-02 05:47:59.007 atags += ' ' * la
2025-07-02 05:47:59.019 btags += ' ' * lb
2025-07-02 05:47:59.028 else:
2025-07-02 05:47:59.041 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:47:59.051 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:47:59.059 else:
2025-07-02 05:47:59.067 # the synch pair is identical
2025-07-02 05:47:59.074 yield '  ' + aelt
2025-07-02 05:47:59.081
2025-07-02 05:47:59.088 # pump out diffs from after the synch point
2025-07-02 05:47:59.094 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:47:59.100
2025-07-02 05:47:59.106 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:47:59.113 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:59.119
2025-07-02 05:47:59.127 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:59.138 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:59.146 alo = 163, ahi = 1101
2025-07-02 05:47:59.154 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:59.163 blo = 163, bhi = 1101
2025-07-02 05:47:59.172
2025-07-02 05:47:59.178 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:59.184 g = []
2025-07-02 05:47:59.190 if alo < ahi:
2025-07-02 05:47:59.201 if blo < bhi:
2025-07-02 05:47:59.211 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:59.219 else:
2025-07-02 05:47:59.229 g = self._dump('-', a, alo, ahi)
2025-07-02 05:47:59.239 elif blo < bhi:
2025-07-02 05:47:59.247 g = self._dump('+', b, blo, bhi)
2025-07-02 05:47:59.257
2025-07-02 05:47:59.268 >       yield from g
2025-07-02 05:47:59.281
2025-07-02 05:47:59.291 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:47:59.298 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:47:59.307
2025-07-02 05:47:59.317 self = <difflib.Differ object at [hex]>
2025-07-02 05:47:59.327 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:47:59.335 alo = 163, ahi = 1101
2025-07-02 05:47:59.342 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:47:59.349 blo = 163, bhi = 1101
2025-07-02 05:47:59.354
2025-07-02 05:47:59.365 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:47:59.375 r"""
2025-07-02 05:47:59.384 When replacing one block of lines with another, search the blocks
2025-07-02 05:47:59.391 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:47:59.397 synch point, and intraline difference marking is done on the
2025-07-02 05:47:59.403 similar pair. Lots of work, but often worth it.
2025-07-02 05:47:59.410
2025-07-02 05:47:59.416 Example:
2025-07-02 05:47:59.423
2025-07-02 05:47:59.431 >>> d = Differ()
2025-07-02 05:47:59.438 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:47:59.451 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:47:59.462 >>> print(''.join(results), end="")
2025-07-02 05:47:59.474 - abcDefghiJkl
2025-07-02 05:47:59.499 + abcdefGhijkl
2025-07-02 05:47:59.525 """
2025-07-02 05:47:59.539
2025-07-02 05:47:59.548 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:47:59.556 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:47:59.565 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:47:59.579 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:47:59.592 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:47:59.601
2025-07-02 05:47:59.609 # search for the pair that matches best without being identical
2025-07-02 05:47:59.617 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:47:59.627 # on junk -- unless we have to)
2025-07-02 05:47:59.635 for j in range(blo, bhi):
2025-07-02 05:47:59.643 bj = b[j]
2025-07-02 05:47:59.653 cruncher.set_seq2(bj)
2025-07-02 05:47:59.663 for i in range(alo, ahi):
2025-07-02 05:47:59.673 ai = a[i]
2025-07-02 05:47:59.683 if ai == bj:
2025-07-02 05:47:59.693 if eqi is None:
2025-07-02 05:47:59.704 eqi, eqj = i, j
2025-07-02 05:47:59.716 continue
2025-07-02 05:47:59.729 cruncher.set_seq1(ai)
2025-07-02 05:47:59.744 # computing similarity is expensive, so use the quick
2025-07-02 05:47:59.759 # upper bounds first -- have seen this speed up messy
2025-07-02 05:47:59.768 # compares by a factor of 3.
2025-07-02 05:47:59.775 # note that ratio() is only expensive to compute the first
2025-07-02 05:47:59.782 # time it's called on a sequence pair; the expensive part
2025-07-02 05:47:59.790 # of the computation is cached by cruncher
2025-07-02 05:47:59.796 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:47:59.802 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:47:59.809 cruncher.ratio() > best_ratio:
2025-07-02 05:47:59.816 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:47:59.823 if best_ratio < cutoff:
2025-07-02 05:47:59.830 # no non-identical "pretty close" pair
2025-07-02 05:47:59.837 if eqi is None:
2025-07-02 05:47:59.845 # no identical pair either -- treat it as a straight replace
2025-07-02 05:47:59.853 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:47:59.860 return
2025-07-02 05:47:59.868 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:47:59.876 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:47:59.883 else:
2025-07-02 05:47:59.891 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:47:59.901 eqi = None
2025-07-02 05:47:59.912
2025-07-02 05:47:59.921 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:47:59.929 # identical
2025-07-02 05:47:59.935
2025-07-02 05:47:59.942 # pump out diffs from before the synch point
2025-07-02 05:47:59.952 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:47:59.958
2025-07-02 05:47:59.965 # do intraline marking on the synch pair
2025-07-02 05:47:59.972 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:47:59.978 if eqi is None:
2025-07-02 05:47:59.984 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:47:59.990 atags = btags = ""
2025-07-02 05:47:59.996 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:00.002 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:00.013 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:00.024 if tag == 'replace':
2025-07-02 05:48:00.032 atags += '^' * la
2025-07-02 05:48:00.039 btags += '^' * lb
2025-07-02 05:48:00.047 elif tag == 'delete':
2025-07-02 05:48:00.058 atags += '-' * la
2025-07-02 05:48:00.065 elif tag == 'insert':
2025-07-02 05:48:00.078 btags += '+' * lb
2025-07-02 05:48:00.089 elif tag == 'equal':
2025-07-02 05:48:00.101 atags += ' ' * la
2025-07-02 05:48:00.111 btags += ' ' * lb
2025-07-02 05:48:00.117 else:
2025-07-02 05:48:00.123 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:00.130 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:00.135 else:
2025-07-02 05:48:00.141 # the synch pair is identical
2025-07-02 05:48:00.147 yield '  ' + aelt
2025-07-02 05:48:00.156
2025-07-02 05:48:00.167 # pump out diffs from after the synch point
2025-07-02 05:48:00.176 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:00.182
2025-07-02 05:48:00.188 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:00.195 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:00.202
2025-07-02 05:48:00.208 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:00.221 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:00.233 alo = 164, ahi = 1101
2025-07-02 05:48:00.243 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:00.249 blo = 164, bhi = 1101
2025-07-02 05:48:00.255
2025-07-02 05:48:00.262 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:00.268 g = []
2025-07-02 05:48:00.275 if alo < ahi:
2025-07-02 05:48:00.286 if blo < bhi:
2025-07-02 05:48:00.294 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:00.300 else:
2025-07-02 05:48:00.306 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:00.316 elif blo < bhi:
2025-07-02 05:48:00.326 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:00.333
2025-07-02 05:48:00.341 >       yield from g
2025-07-02 05:48:00.349
2025-07-02 05:48:00.356 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:00.364 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:00.370
2025-07-02 05:48:00.379 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:00.391 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:00.398 alo = 164, ahi = 1101
2025-07-02 05:48:00.405 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:00.410 blo = 164, bhi = 1101
2025-07-02 05:48:00.419
2025-07-02 05:48:00.428 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:00.435 r"""
2025-07-02 05:48:00.442 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:00.451 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:00.461 synch point, and intraline difference marking is done on the
2025-07-02 05:48:00.473 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:00.479
2025-07-02 05:48:00.486 Example:
2025-07-02 05:48:00.492
2025-07-02 05:48:00.499 >>> d = Differ()
2025-07-02 05:48:00.506 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:00.519 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:00.531 >>> print(''.join(results), end="")
2025-07-02 05:48:00.540 - abcDefghiJkl
2025-07-02 05:48:00.553 + abcdefGhijkl
2025-07-02 05:48:00.573 """
2025-07-02 05:48:00.580
2025-07-02 05:48:00.587 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:00.593 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:00.606 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:00.618 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:00.627 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:00.635
2025-07-02 05:48:00.642 # search for the pair that matches best without being identical
2025-07-02 05:48:00.653 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:00.662 # on junk -- unless we have to)
2025-07-02 05:48:00.671 for j in range(blo, bhi):
2025-07-02 05:48:00.677 bj = b[j]
2025-07-02 05:48:00.688 cruncher.set_seq2(bj)
2025-07-02 05:48:00.698 for i in range(alo, ahi):
2025-07-02 05:48:00.707 ai = a[i]
2025-07-02 05:48:00.718 if ai == bj:
2025-07-02 05:48:00.727 if eqi is None:
2025-07-02 05:48:00.737 eqi, eqj = i, j
2025-07-02 05:48:00.749 continue
2025-07-02 05:48:00.760 cruncher.set_seq1(ai)
2025-07-02 05:48:00.769 # computing similarity is expensive, so use the quick
2025-07-02 05:48:00.777 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:00.783 # compares by a factor of 3.
2025-07-02 05:48:00.791 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:00.800 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:00.811 # of the computation is cached by cruncher
2025-07-02 05:48:00.819 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:00.825 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:00.830 cruncher.ratio() > best_ratio:
2025-07-02 05:48:00.836 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:00.843 if best_ratio < cutoff:
2025-07-02 05:48:00.853 # no non-identical "pretty close" pair
2025-07-02 05:48:00.861 if eqi is None:
2025-07-02 05:48:00.872 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:00.881 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:00.889 return
2025-07-02 05:48:00.896 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:00.902 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:00.908 else:
2025-07-02 05:48:00.914 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:00.925 eqi = None
2025-07-02 05:48:00.934
2025-07-02 05:48:00.943 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:00.952 # identical
2025-07-02 05:48:00.962
2025-07-02 05:48:00.970 # pump out diffs from before the synch point
2025-07-02 05:48:00.978 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:00.991
2025-07-02 05:48:01.000 # do intraline marking on the synch pair
2025-07-02 05:48:01.008 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:01.014 if eqi is None:
2025-07-02 05:48:01.023 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:01.034 atags = btags = ""
2025-07-02 05:48:01.041 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:01.048 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:01.053 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:01.058 if tag == 'replace':
2025-07-02 05:48:01.063 atags += '^' * la
2025-07-02 05:48:01.068 btags += '^' * lb
2025-07-02 05:48:01.073 elif tag == 'delete':
2025-07-02 05:48:01.078 atags += '-' * la
2025-07-02 05:48:01.085 elif tag == 'insert':
2025-07-02 05:48:01.092 btags += '+' * lb
2025-07-02 05:48:01.098 elif tag == 'equal':
2025-07-02 05:48:01.107 atags += ' ' * la
2025-07-02 05:48:01.115 btags += ' ' * lb
2025-07-02 05:48:01.123 else:
2025-07-02 05:48:01.131 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:01.138 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:01.145 else:
2025-07-02 05:48:01.155 # the synch pair is identical
2025-07-02 05:48:01.169 yield '  ' + aelt
2025-07-02 05:48:01.180
2025-07-02 05:48:01.190 # pump out diffs from after the synch point
2025-07-02 05:48:01.197 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:01.203
2025-07-02 05:48:01.210 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:01.217 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:01.224
2025-07-02 05:48:01.231 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:01.243 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:01.253 alo = 165, ahi = 1101
2025-07-02 05:48:01.262 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:01.268 blo = 165, bhi = 1101
2025-07-02 05:48:01.274
2025-07-02 05:48:01.281 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:01.287 g = []
2025-07-02 05:48:01.296 if alo < ahi:
2025-07-02 05:48:01.306 if blo < bhi:
2025-07-02 05:48:01.315 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:01.322 else:
2025-07-02 05:48:01.329 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:01.337 elif blo < bhi:
2025-07-02 05:48:01.345 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:01.353
2025-07-02 05:48:01.360 >       yield from g
2025-07-02 05:48:01.367
2025-07-02 05:48:01.375 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:01.382 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:01.391
2025-07-02 05:48:01.404 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:01.414 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:01.423 alo = 165, ahi = 1101
2025-07-02 05:48:01.435 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:01.442 blo = 165, bhi = 1101
2025-07-02 05:48:01.448
2025-07-02 05:48:01.455 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:01.463 r"""
2025-07-02 05:48:01.470 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:01.477 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:01.484 synch point, and intraline difference marking is done on the
2025-07-02 05:48:01.490 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:01.496
2025-07-02 05:48:01.503 Example:
2025-07-02 05:48:01.509
2025-07-02 05:48:01.515 >>> d = Differ()
2025-07-02 05:48:01.521 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:01.527 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:01.534 >>> print(''.join(results), end="")
2025-07-02 05:48:01.544 - abcDefghiJkl
2025-07-02 05:48:01.563 + abcdefGhijkl
2025-07-02 05:48:01.582 """
2025-07-02 05:48:01.591
2025-07-02 05:48:01.600 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:01.611 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:01.622 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:01.635 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:01.648 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:01.661
2025-07-02 05:48:01.671 # search for the pair that matches best without being identical
2025-07-02 05:48:01.680 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:01.693 # on junk -- unless we have to)
2025-07-02 05:48:01.707 for j in range(blo, bhi):
2025-07-02 05:48:01.718 bj = b[j]
2025-07-02 05:48:01.726 cruncher.set_seq2(bj)
2025-07-02 05:48:01.735 for i in range(alo, ahi):
2025-07-02 05:48:01.743 ai = a[i]
2025-07-02 05:48:01.750 if ai == bj:
2025-07-02 05:48:01.758 if eqi is None:
2025-07-02 05:48:01.766 eqi, eqj = i, j
2025-07-02 05:48:01.773 continue
2025-07-02 05:48:01.780 cruncher.set_seq1(ai)
2025-07-02 05:48:01.787 # computing similarity is expensive, so use the quick
2025-07-02 05:48:01.795 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:01.805 # compares by a factor of 3.
2025-07-02 05:48:01.813 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:01.826 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:01.837 # of the computation is cached by cruncher
2025-07-02 05:48:01.844 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:01.852 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:01.859 cruncher.ratio() > best_ratio:
2025-07-02 05:48:01.865 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:01.870 if best_ratio < cutoff:
2025-07-02 05:48:01.875 # no non-identical "pretty close" pair
2025-07-02 05:48:01.881 if eqi is None:
2025-07-02 05:48:01.888 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:01.894 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:01.900 return
2025-07-02 05:48:01.905 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:01.909 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:01.914 else:
2025-07-02 05:48:01.919 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:01.925 eqi = None
2025-07-02 05:48:01.930
2025-07-02 05:48:01.937 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:01.943 # identical
2025-07-02 05:48:01.949
2025-07-02 05:48:01.956 # pump out diffs from before the synch point
2025-07-02 05:48:01.970 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:01.979
2025-07-02 05:48:01.990 # do intraline marking on the synch pair
2025-07-02 05:48:02.003 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:02.017 if eqi is None:
2025-07-02 05:48:02.026 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:02.034 atags = btags = ""
2025-07-02 05:48:02.040 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:02.047 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:02.055 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:02.063 if tag == 'replace':
2025-07-02 05:48:02.070 atags += '^' * la
2025-07-02 05:48:02.078 btags += '^' * lb
2025-07-02 05:48:02.086 elif tag == 'delete':
2025-07-02 05:48:02.096 atags += '-' * la
2025-07-02 05:48:02.107 elif tag == 'insert':
2025-07-02 05:48:02.114 btags += '+' * lb
2025-07-02 05:48:02.122 elif tag == 'equal':
2025-07-02 05:48:02.129 atags += ' ' * la
2025-07-02 05:48:02.140 btags += ' ' * lb
2025-07-02 05:48:02.151 else:
2025-07-02 05:48:02.161 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:02.170 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:02.183 else:
2025-07-02 05:48:02.196 # the synch pair is identical
2025-07-02 05:48:02.206 yield '  ' + aelt
2025-07-02 05:48:02.216
2025-07-02 05:48:02.227 # pump out diffs from after the synch point
2025-07-02 05:48:02.236 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:02.244
2025-07-02 05:48:02.251 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:02.261 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:02.268
2025-07-02 05:48:02.275 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:02.282 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:02.288 alo = 166, ahi = 1101
2025-07-02 05:48:02.295 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:02.301 blo = 166, bhi = 1101
2025-07-02 05:48:02.306
2025-07-02 05:48:02.312 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:02.317 g = []
2025-07-02 05:48:02.324 if alo < ahi:
2025-07-02 05:48:02.335 if blo < bhi:
2025-07-02 05:48:02.345 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:02.355 else:
2025-07-02 05:48:02.366 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:02.373 elif blo < bhi:
2025-07-02 05:48:02.380 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:02.386
2025-07-02 05:48:02.398 >       yield from g
2025-07-02 05:48:02.408
2025-07-02 05:48:02.416 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:02.422 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:02.429
2025-07-02 05:48:02.436 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:02.450 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:02.460 alo = 166, ahi = 1101
2025-07-02 05:48:02.468 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:02.473 blo = 166, bhi = 1101
2025-07-02 05:48:02.479
2025-07-02 05:48:02.485 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:02.492 r"""
2025-07-02 05:48:02.499 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:02.507 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:02.515 synch point, and intraline difference marking is done on the
2025-07-02 05:48:02.522 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:02.531
2025-07-02 05:48:02.541 Example:
2025-07-02 05:48:02.548
2025-07-02 05:48:02.553 >>> d = Differ()
2025-07-02 05:48:02.566 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:02.576 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:02.582 >>> print(''.join(results), end="")
2025-07-02 05:48:02.589 - abcDefghiJkl
2025-07-02 05:48:02.603 + abcdefGhijkl
2025-07-02 05:48:02.619 """
2025-07-02 05:48:02.628
2025-07-02 05:48:02.636 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:02.644 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:02.650 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:02.670 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:02.682 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:02.692
2025-07-02 05:48:02.700 # search for the pair that matches best without being identical
2025-07-02 05:48:02.708 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:02.715 # on junk -- unless we have to)
2025-07-02 05:48:02.721 for j in range(blo, bhi):
2025-07-02 05:48:02.726 bj = b[j]
2025-07-02 05:48:02.731 cruncher.set_seq2(bj)
2025-07-02 05:48:02.740 for i in range(alo, ahi):
2025-07-02 05:48:02.751 ai = a[i]
2025-07-02 05:48:02.761 if ai == bj:
2025-07-02 05:48:02.769 if eqi is None:
2025-07-02 05:48:02.775 eqi, eqj = i, j
2025-07-02 05:48:02.782 continue
2025-07-02 05:48:02.790 cruncher.set_seq1(ai)
2025-07-02 05:48:02.797 # computing similarity is expensive, so use the quick
2025-07-02 05:48:02.804 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:02.816 # compares by a factor of 3.
2025-07-02 05:48:02.829 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:02.835 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:02.842 # of the computation is cached by cruncher
2025-07-02 05:48:02.855 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:02.870 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:02.880 cruncher.ratio() > best_ratio:
2025-07-02 05:48:02.887 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:02.897 if best_ratio < cutoff:
2025-07-02 05:48:02.907 # no non-identical "pretty close" pair
2025-07-02 05:48:02.916 if eqi is None:
2025-07-02 05:48:02.925 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:02.933 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:02.941 return
2025-07-02 05:48:02.954 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:02.965 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:02.973 else:
2025-07-02 05:48:02.987 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:02.996 eqi = None
2025-07-02 05:48:03.007
2025-07-02 05:48:03.015 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:03.022 # identical
2025-07-02 05:48:03.028
2025-07-02 05:48:03.034 # pump out diffs from before the synch point
2025-07-02 05:48:03.042 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:03.054
2025-07-02 05:48:03.065 # do intraline marking on the synch pair
2025-07-02 05:48:03.072 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:03.079 if eqi is None:
2025-07-02 05:48:03.086 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:03.093 atags = btags = ""
2025-07-02 05:48:03.099 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:03.105 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:03.116 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:03.123 if tag == 'replace':
2025-07-02 05:48:03.133 atags += '^' * la
2025-07-02 05:48:03.145 btags += '^' * lb
2025-07-02 05:48:03.156 elif tag == 'delete':
2025-07-02 05:48:03.170 atags += '-' * la
2025-07-02 05:48:03.180 elif tag == 'insert':
2025-07-02 05:48:03.188 btags += '+' * lb
2025-07-02 05:48:03.196 elif tag == 'equal':
2025-07-02 05:48:03.202 atags += ' ' * la
2025-07-02 05:48:03.207 btags += ' ' * lb
2025-07-02 05:48:03.214 else:
2025-07-02 05:48:03.220 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:03.228 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:03.237 else:
2025-07-02 05:48:03.245 # the synch pair is identical
2025-07-02 05:48:03.253 yield '  ' + aelt
2025-07-02 05:48:03.260
2025-07-02 05:48:03.269 # pump out diffs from after the synch point
2025-07-02 05:48:03.276 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:03.284
2025-07-02 05:48:03.297 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:03.308 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:03.317
2025-07-02 05:48:03.329 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:03.342 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:03.353 alo = 167, ahi = 1101
2025-07-02 05:48:03.366 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:03.375 blo = 167, bhi = 1101
2025-07-02 05:48:03.383
2025-07-02 05:48:03.394 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:03.403 g = []
2025-07-02 05:48:03.409 if alo < ahi:
2025-07-02 05:48:03.416 if blo < bhi:
2025-07-02 05:48:03.423 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:03.431 else:
2025-07-02 05:48:03.440 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:03.448 elif blo < bhi:
2025-07-02 05:48:03.455 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:03.469
2025-07-02 05:48:03.478 >       yield from g
2025-07-02 05:48:03.485
2025-07-02 05:48:03.491 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:03.499 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:03.506
2025-07-02 05:48:03.518 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:03.529 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:03.541 alo = 167, ahi = 1101
2025-07-02 05:48:03.550 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:03.558 blo = 167, bhi = 1101
2025-07-02 05:48:03.565
2025-07-02 05:48:03.577 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:03.587 r"""
2025-07-02 05:48:03.595 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:03.601 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:03.607 synch point, and intraline difference marking is done on the
2025-07-02 05:48:03.613 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:03.623
2025-07-02 05:48:03.636 Example:
2025-07-02 05:48:03.645
2025-07-02 05:48:03.653 >>> d = Differ()
2025-07-02 05:48:03.660 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:03.666 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:03.672 >>> print(''.join(results), end="")
2025-07-02 05:48:03.679 - abcDefghiJkl
2025-07-02 05:48:03.698 + abcdefGhijkl
2025-07-02 05:48:03.713 """
2025-07-02 05:48:03.723
2025-07-02 05:48:03.734 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:03.743 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:03.750 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:03.756 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:03.763 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:03.769
2025-07-02 05:48:03.776 # search for the pair that matches best without being identical
2025-07-02 05:48:03.782 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:03.791 # on junk -- unless we have to)
2025-07-02 05:48:03.800 for j in range(blo, bhi):
2025-07-02 05:48:03.807 bj = b[j]
2025-07-02 05:48:03.814 cruncher.set_seq2(bj)
2025-07-02 05:48:03.821 for i in range(alo, ahi):
2025-07-02 05:48:03.826 ai = a[i]
2025-07-02 05:48:03.831 if ai == bj:
2025-07-02 05:48:03.837 if eqi is None:
2025-07-02 05:48:03.843 eqi, eqj = i, j
2025-07-02 05:48:03.850 continue
2025-07-02 05:48:03.856 cruncher.set_seq1(ai)
2025-07-02 05:48:03.864 # computing similarity is expensive, so use the quick
2025-07-02 05:48:03.872 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:03.881 # compares by a factor of 3.
2025-07-02 05:48:03.894 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:03.906 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:03.915 # of the computation is cached by cruncher
2025-07-02 05:48:03.922 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:03.927 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:03.932 cruncher.ratio() > best_ratio:
2025-07-02 05:48:03.938 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:03.944 if best_ratio < cutoff:
2025-07-02 05:48:03.950 # no non-identical "pretty close" pair
2025-07-02 05:48:03.963 if eqi is None:
2025-07-02 05:48:03.974 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:03.982 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:03.988 return
2025-07-02 05:48:03.998 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:04.009 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:04.017 else:
2025-07-02 05:48:04.025 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:04.031 eqi = None
2025-07-02 05:48:04.037
2025-07-02 05:48:04.042 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:04.048 # identical
2025-07-02 05:48:04.053
2025-07-02 05:48:04.060 # pump out diffs from before the synch point
2025-07-02 05:48:04.067 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:04.073
2025-07-02 05:48:04.079 # do intraline marking on the synch pair
2025-07-02 05:48:04.091 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:04.101 if eqi is None:
2025-07-02 05:48:04.113 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:04.122 atags = btags = ""
2025-07-02 05:48:04.134 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:04.144 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:04.152 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:04.162 if tag == 'replace':
2025-07-02 05:48:04.174 atags += '^' * la
2025-07-02 05:48:04.185 btags += '^' * lb
2025-07-02 05:48:04.197 elif tag == 'delete':
2025-07-02 05:48:04.208 atags += '-' * la
2025-07-02 05:48:04.217 elif tag == 'insert':
2025-07-02 05:48:04.224 btags += '+' * lb
2025-07-02 05:48:04.231 elif tag == 'equal':
2025-07-02 05:48:04.246 atags += ' ' * la
2025-07-02 05:48:04.254 btags += ' ' * lb
2025-07-02 05:48:04.267 else:
2025-07-02 05:48:04.282 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:04.295 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:04.304 else:
2025-07-02 05:48:04.312 # the synch pair is identical
2025-07-02 05:48:04.320 yield '  ' + aelt
2025-07-02 05:48:04.332
2025-07-02 05:48:04.344 # pump out diffs from after the synch point
2025-07-02 05:48:04.354 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:04.363
2025-07-02 05:48:04.369 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:04.376 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:04.384
2025-07-02 05:48:04.395 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:04.408 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:04.418 alo = 168, ahi = 1101
2025-07-02 05:48:04.428 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:04.441 blo = 168, bhi = 1101
2025-07-02 05:48:04.454
2025-07-02 05:48:04.468 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:04.478 g = []
2025-07-02 05:48:04.489 if alo < ahi:
2025-07-02 05:48:04.503 if blo < bhi:
2025-07-02 05:48:04.512 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:04.519 else:
2025-07-02 05:48:04.526 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:04.534 elif blo < bhi:
2025-07-02 05:48:04.542 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:04.550
2025-07-02 05:48:04.558 >       yield from g
2025-07-02 05:48:04.569
2025-07-02 05:48:04.582 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:04.590 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:04.596
2025-07-02 05:48:04.603 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:04.611 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:04.622 alo = 168, ahi = 1101
2025-07-02 05:48:04.633 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:04.640 blo = 168, bhi = 1101
2025-07-02 05:48:04.650
2025-07-02 05:48:04.659 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:04.668 r"""
2025-07-02 05:48:04.677 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:04.685 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:04.696 synch point, and intraline difference marking is done on the
2025-07-02 05:48:04.709 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:04.718
2025-07-02 05:48:04.728 Example:
2025-07-02 05:48:04.741
2025-07-02 05:48:04.751 >>> d = Differ()
2025-07-02 05:48:04.762 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:04.770 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:04.780 >>> print(''.join(results), end="")
2025-07-02 05:48:04.787 - abcDefghiJkl
2025-07-02 05:48:04.804 + abcdefGhijkl
2025-07-02 05:48:04.816 """
2025-07-02 05:48:04.822
2025-07-02 05:48:04.829 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:04.836 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:04.843 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:04.849 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:04.854 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:04.859
2025-07-02 05:48:04.865 # search for the pair that matches best without being identical
2025-07-02 05:48:04.874 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:04.882 # on junk -- unless we have to)
2025-07-02 05:48:04.889 for j in range(blo, bhi):
2025-07-02 05:48:04.896 bj = b[j]
2025-07-02 05:48:04.903 cruncher.set_seq2(bj)
2025-07-02 05:48:04.911 for i in range(alo, ahi):
2025-07-02 05:48:04.919 ai = a[i]
2025-07-02 05:48:04.927 if ai == bj:
2025-07-02 05:48:04.934 if eqi is None:
2025-07-02 05:48:04.943 eqi, eqj = i, j
2025-07-02 05:48:04.955 continue
2025-07-02 05:48:04.962 cruncher.set_seq1(ai)
2025-07-02 05:48:04.970 # computing similarity is expensive, so use the quick
2025-07-02 05:48:04.979 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:04.987 # compares by a factor of 3.
2025-07-02 05:48:04.996 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:05.004 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:05.012 # of the computation is cached by cruncher
2025-07-02 05:48:05.019 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:05.026 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:05.037 cruncher.ratio() > best_ratio:
2025-07-02 05:48:05.048 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:05.056 if best_ratio < cutoff:
2025-07-02 05:48:05.064 # no non-identical "pretty close" pair
2025-07-02 05:48:05.071 if eqi is None:
2025-07-02 05:48:05.077 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:05.084 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:05.090 return
2025-07-02 05:48:05.096 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:05.101 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:05.107 else:
2025-07-02 05:48:05.113 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:05.119 eqi = None
2025-07-02 05:48:05.125
2025-07-02 05:48:05.131 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:05.136 # identical
2025-07-02 05:48:05.141
2025-07-02 05:48:05.146 # pump out diffs from before the synch point
2025-07-02 05:48:05.151 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:05.158
2025-07-02 05:48:05.171 # do intraline marking on the synch pair
2025-07-02 05:48:05.183 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:05.196 if eqi is None:
2025-07-02 05:48:05.213 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:05.223 atags = btags = ""
2025-07-02 05:48:05.231 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:05.239 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:05.246 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:05.253 if tag == 'replace':
2025-07-02 05:48:05.260 atags += '^' * la
2025-07-02 05:48:05.266 btags += '^' * lb
2025-07-02 05:48:05.271 elif tag == 'delete':
2025-07-02 05:48:05.276 atags += '-' * la
2025-07-02 05:48:05.282 elif tag == 'insert':
2025-07-02 05:48:05.287 btags += '+' * lb
2025-07-02 05:48:05.292 elif tag == 'equal':
2025-07-02 05:48:05.299 atags += ' ' * la
2025-07-02 05:48:05.307 btags += ' ' * lb
2025-07-02 05:48:05.319 else:
2025-07-02 05:48:05.328 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:05.336 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:05.343 else:
2025-07-02 05:48:05.350 # the synch pair is identical
2025-07-02 05:48:05.355 yield '  ' + aelt
2025-07-02 05:48:05.361
2025-07-02 05:48:05.370 # pump out diffs from after the synch point
2025-07-02 05:48:05.377 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:05.390
2025-07-02 05:48:05.404 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:05.412 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:05.423
2025-07-02 05:48:05.432 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:05.442 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:05.449 alo = 169, ahi = 1101
2025-07-02 05:48:05.456 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:05.462 blo = 169, bhi = 1101
2025-07-02 05:48:05.466
2025-07-02 05:48:05.472 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:05.478 g = []
2025-07-02 05:48:05.484 if alo < ahi:
2025-07-02 05:48:05.490 if blo < bhi:
2025-07-02 05:48:05.498 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:05.510 else:
2025-07-02 05:48:05.519 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:05.526 elif blo < bhi:
2025-07-02 05:48:05.533 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:05.541
2025-07-02 05:48:05.548 >       yield from g
2025-07-02 05:48:05.554
2025-07-02 05:48:05.560 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:05.570 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:05.576
2025-07-02 05:48:05.584 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:05.592 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:05.599 alo = 169, ahi = 1101
2025-07-02 05:48:05.606 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:05.613 blo = 169, bhi = 1101
2025-07-02 05:48:05.618
2025-07-02 05:48:05.623 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:05.628 r"""
2025-07-02 05:48:05.636 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:05.643 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:05.650 synch point, and intraline difference marking is done on the
2025-07-02 05:48:05.659 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:05.666
2025-07-02 05:48:05.672 Example:
2025-07-02 05:48:05.679
2025-07-02 05:48:05.688 >>> d = Differ()
2025-07-02 05:48:05.695 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:05.704 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:05.714 >>> print(''.join(results), end="")
2025-07-02 05:48:05.728 - abcDefghiJkl
2025-07-02 05:48:05.752 + abcdefGhijkl
2025-07-02 05:48:05.773 """
2025-07-02 05:48:05.786
2025-07-02 05:48:05.796 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:05.804 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:05.811 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:05.817 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:05.829 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:05.838
2025-07-02 05:48:05.847 # search for the pair that matches best without being identical
2025-07-02 05:48:05.855 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:05.862 # on junk -- unless we have to)
2025-07-02 05:48:05.868 for j in range(blo, bhi):
2025-07-02 05:48:05.875 bj = b[j]
2025-07-02 05:48:05.881 cruncher.set_seq2(bj)
2025-07-02 05:48:05.887 for i in range(alo, ahi):
2025-07-02 05:48:05.893 ai = a[i]
2025-07-02 05:48:05.905 if ai == bj:
2025-07-02 05:48:05.915 if eqi is None:
2025-07-02 05:48:05.922 eqi, eqj = i, j
2025-07-02 05:48:05.932 continue
2025-07-02 05:48:05.941 cruncher.set_seq1(ai)
2025-07-02 05:48:05.948 # computing similarity is expensive, so use the quick
2025-07-02 05:48:05.955 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:05.962 # compares by a factor of 3.
2025-07-02 05:48:05.973 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:05.981 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:05.988 # of the computation is cached by cruncher
2025-07-02 05:48:05.994 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:05.999 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:06.006 cruncher.ratio() > best_ratio:
2025-07-02 05:48:06.012 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:06.018 if best_ratio < cutoff:
2025-07-02 05:48:06.029 # no non-identical "pretty close" pair
2025-07-02 05:48:06.039 if eqi is None:
2025-07-02 05:48:06.048 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:06.056 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:06.063 return
2025-07-02 05:48:06.069 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:06.076 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:06.082 else:
2025-07-02 05:48:06.088 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:06.094 eqi = None
2025-07-02 05:48:06.099
2025-07-02 05:48:06.106 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:06.119 # identical
2025-07-02 05:48:06.132
2025-07-02 05:48:06.143 # pump out diffs from before the synch point
2025-07-02 05:48:06.154 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:06.166
2025-07-02 05:48:06.176 # do intraline marking on the synch pair
2025-07-02 05:48:06.184 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:06.191 if eqi is None:
2025-07-02 05:48:06.199 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:06.207 atags = btags = ""
2025-07-02 05:48:06.219 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:06.227 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:06.233 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:06.239 if tag == 'replace':
2025-07-02 05:48:06.244 atags += '^' * la
2025-07-02 05:48:06.250 btags += '^' * lb
2025-07-02 05:48:06.260 elif tag == 'delete':
2025-07-02 05:48:06.270 atags += '-' * la
2025-07-02 05:48:06.286 elif tag == 'insert':
2025-07-02 05:48:06.296 btags += '+' * lb
2025-07-02 05:48:06.308 elif tag == 'equal':
2025-07-02 05:48:06.317 atags += ' ' * la
2025-07-02 05:48:06.325 btags += ' ' * lb
2025-07-02 05:48:06.331 else:
2025-07-02 05:48:06.338 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:06.345 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:06.356 else:
2025-07-02 05:48:06.365 # the synch pair is identical
2025-07-02 05:48:06.372 yield '  ' + aelt
2025-07-02 05:48:06.378
2025-07-02 05:48:06.385 # pump out diffs from after the synch point
2025-07-02 05:48:06.394 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:06.401
2025-07-02 05:48:06.407 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:06.413 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:06.418
2025-07-02 05:48:06.424 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:06.432 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:06.439 alo = 170, ahi = 1101
2025-07-02 05:48:06.446 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:06.454 blo = 170, bhi = 1101
2025-07-02 05:48:06.466
2025-07-02 05:48:06.478 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:06.487 g = []
2025-07-02 05:48:06.494 if alo < ahi:
2025-07-02 05:48:06.501 if blo < bhi:
2025-07-02 05:48:06.512 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:06.525 else:
2025-07-02 05:48:06.539 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:06.548 elif blo < bhi:
2025-07-02 05:48:06.556 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:06.563
2025-07-02 05:48:06.572 >       yield from g
2025-07-02 05:48:06.583
2025-07-02 05:48:06.590 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:06.603 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:06.616
2025-07-02 05:48:06.628 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:06.639 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:06.651 alo = 170, ahi = 1101
2025-07-02 05:48:06.663 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:06.671 blo = 170, bhi = 1101
2025-07-02 05:48:06.681
2025-07-02 05:48:06.688 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:06.696 r"""
2025-07-02 05:48:06.703 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:06.709 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:06.716 synch point, and intraline difference marking is done on the
2025-07-02 05:48:06.722 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:06.728
2025-07-02 05:48:06.734 Example:
2025-07-02 05:48:06.751
2025-07-02 05:48:06.757 >>> d = Differ()
2025-07-02 05:48:06.763 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:06.769 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:06.775 >>> print(''.join(results), end="")
2025-07-02 05:48:06.783 - abcDefghiJkl
2025-07-02 05:48:06.804 + abcdefGhijkl
2025-07-02 05:48:06.825 """
2025-07-02 05:48:06.833
2025-07-02 05:48:06.841 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:06.848 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:06.855 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:06.862 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:06.874 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:06.884
2025-07-02 05:48:06.894 # search for the pair that matches best without being identical
2025-07-02 05:48:06.900 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:06.907 # on junk -- unless we have to)
2025-07-02 05:48:06.913 for j in range(blo, bhi):
2025-07-02 05:48:06.920 bj = b[j]
2025-07-02 05:48:06.926 cruncher.set_seq2(bj)
2025-07-02 05:48:06.938 for i in range(alo, ahi):
2025-07-02 05:48:06.948 ai = a[i]
2025-07-02 05:48:06.955 if ai == bj:
2025-07-02 05:48:06.962 if eqi is None:
2025-07-02 05:48:06.969 eqi, eqj = i, j
2025-07-02 05:48:06.975 continue
2025-07-02 05:48:06.982 cruncher.set_seq1(ai)
2025-07-02 05:48:06.989 # computing similarity is expensive, so use the quick
2025-07-02 05:48:06.995 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:07.001 # compares by a factor of 3.
2025-07-02 05:48:07.007 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:07.014 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:07.020 # of the computation is cached by cruncher
2025-07-02 05:48:07.027 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:07.036 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:07.042 cruncher.ratio() > best_ratio:
2025-07-02 05:48:07.049 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:07.055 if best_ratio < cutoff:
2025-07-02 05:48:07.060 # no non-identical "pretty close" pair
2025-07-02 05:48:07.067 if eqi is None:
2025-07-02 05:48:07.079 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:07.088 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:07.098 return
2025-07-02 05:48:07.113 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:07.121 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:07.128 else:
2025-07-02 05:48:07.135 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:07.143 eqi = None
2025-07-02 05:48:07.154
2025-07-02 05:48:07.163 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:07.174 # identical
2025-07-02 05:48:07.185
2025-07-02 05:48:07.193 # pump out diffs from before the synch point
2025-07-02 05:48:07.207 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:07.216
2025-07-02 05:48:07.224 # do intraline marking on the synch pair
2025-07-02 05:48:07.231 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:07.245 if eqi is None:
2025-07-02 05:48:07.257 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:07.266 atags = btags = ""
2025-07-02 05:48:07.274 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:07.285 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:07.296 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:07.304 if tag == 'replace':
2025-07-02 05:48:07.311 atags += '^' * la
2025-07-02 05:48:07.317 btags += '^' * lb
2025-07-02 05:48:07.327 elif tag == 'delete':
2025-07-02 05:48:07.333 atags += '-' * la
2025-07-02 05:48:07.341 elif tag == 'insert':
2025-07-02 05:48:07.347 btags += '+' * lb
2025-07-02 05:48:07.354 elif tag == 'equal':
2025-07-02 05:48:07.366 atags += ' ' * la
2025-07-02 05:48:07.375 btags += ' ' * lb
2025-07-02 05:48:07.382 else:
2025-07-02 05:48:07.388 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:07.395 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:07.401 else:
2025-07-02 05:48:07.408 # the synch pair is identical
2025-07-02 05:48:07.414 yield '  ' + aelt
2025-07-02 05:48:07.425
2025-07-02 05:48:07.435 # pump out diffs from after the synch point
2025-07-02 05:48:07.444 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:07.451
2025-07-02 05:48:07.457 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:07.462 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:07.467
2025-07-02 05:48:07.472 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:07.477 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:07.482 alo = 171, ahi = 1101
2025-07-02 05:48:07.487 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:07.496 blo = 171, bhi = 1101
2025-07-02 05:48:07.506
2025-07-02 05:48:07.512 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:07.519 g = []
2025-07-02 05:48:07.525 if alo < ahi:
2025-07-02 05:48:07.537 if blo < bhi:
2025-07-02 05:48:07.547 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:07.555 else:
2025-07-02 05:48:07.562 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:07.567 elif blo < bhi:
2025-07-02 05:48:07.573 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:07.579
2025-07-02 05:48:07.586 >       yield from g
2025-07-02 05:48:07.595
2025-07-02 05:48:07.602 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:07.609 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:07.614
2025-07-02 05:48:07.620 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:07.628 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:07.634 alo = 171, ahi = 1101
2025-07-02 05:48:07.642 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:07.651 blo = 171, bhi = 1101
2025-07-02 05:48:07.663
2025-07-02 05:48:07.673 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:07.681 r"""
2025-07-02 05:48:07.694 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:07.708 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:07.720 synch point, and intraline difference marking is done on the
2025-07-02 05:48:07.731 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:07.741
2025-07-02 05:48:07.748 Example:
2025-07-02 05:48:07.756
2025-07-02 05:48:07.762 >>> d = Differ()
2025-07-02 05:48:07.772 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:07.781 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:07.791 >>> print(''.join(results), end="")
2025-07-02 05:48:07.800 - abcDefghiJkl
2025-07-02 05:48:07.812 + abcdefGhijkl
2025-07-02 05:48:07.823 """
2025-07-02 05:48:07.832
2025-07-02 05:48:07.839 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:07.846 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:07.853 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:07.860 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:07.868 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:07.876
2025-07-02 05:48:07.884 # search for the pair that matches best without being identical
2025-07-02 05:48:07.893 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:07.901 # on junk -- unless we have to)
2025-07-02 05:48:07.908 for j in range(blo, bhi):
2025-07-02 05:48:07.915 bj = b[j]
2025-07-02 05:48:07.921 cruncher.set_seq2(bj)
2025-07-02 05:48:07.927 for i in range(alo, ahi):
2025-07-02 05:48:07.933 ai = a[i]
2025-07-02 05:48:07.938 if ai == bj:
2025-07-02 05:48:07.945 if eqi is None:
2025-07-02 05:48:07.952 eqi, eqj = i, j
2025-07-02 05:48:07.959 continue
2025-07-02 05:48:07.966 cruncher.set_seq1(ai)
2025-07-02 05:48:07.973 # computing similarity is expensive, so use the quick
2025-07-02 05:48:07.981 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:07.988 # compares by a factor of 3.
2025-07-02 05:48:07.993 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:07.998 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:08.003 # of the computation is cached by cruncher
2025-07-02 05:48:08.011 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:08.018 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:08.027 cruncher.ratio() > best_ratio:
2025-07-02 05:48:08.038 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:08.045 if best_ratio < cutoff:
2025-07-02 05:48:08.052 # no non-identical "pretty close" pair
2025-07-02 05:48:08.059 if eqi is None:
2025-07-02 05:48:08.067 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:08.076 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:08.088 return
2025-07-02 05:48:08.096 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:08.104 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:08.113 else:
2025-07-02 05:48:08.128 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:08.136 eqi = None
2025-07-02 05:48:08.143
2025-07-02 05:48:08.150 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:08.166 # identical
2025-07-02 05:48:08.178
2025-07-02 05:48:08.188 # pump out diffs from before the synch point
2025-07-02 05:48:08.199 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:08.208
2025-07-02 05:48:08.218 # do intraline marking on the synch pair
2025-07-02 05:48:08.228 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:08.238 if eqi is None:
2025-07-02 05:48:08.247 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:08.254 atags = btags = ""
2025-07-02 05:48:08.266 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:08.279 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:08.294 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:08.302 if tag == 'replace':
2025-07-02 05:48:08.314 atags += '^' * la
2025-07-02 05:48:08.329 btags += '^' * lb
2025-07-02 05:48:08.337 elif tag == 'delete':
2025-07-02 05:48:08.342 atags += '-' * la
2025-07-02 05:48:08.348 elif tag == 'insert':
2025-07-02 05:48:08.354 btags += '+' * lb
2025-07-02 05:48:08.359 elif tag == 'equal':
2025-07-02 05:48:08.366 atags += ' ' * la
2025-07-02 05:48:08.373 btags += ' ' * lb
2025-07-02 05:48:08.380 else:
2025-07-02 05:48:08.390 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:08.401 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:08.408 else:
2025-07-02 05:48:08.414 # the synch pair is identical
2025-07-02 05:48:08.423 yield '  ' + aelt
2025-07-02 05:48:08.435
2025-07-02 05:48:08.445 # pump out diffs from after the synch point
2025-07-02 05:48:08.459 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:08.468
2025-07-02 05:48:08.476 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:08.484 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:08.494
2025-07-02 05:48:08.505 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:08.514 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:08.522 alo = 172, ahi = 1101
2025-07-02 05:48:08.532 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:08.538 blo = 172, bhi = 1101
2025-07-02 05:48:08.549
2025-07-02 05:48:08.559 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:08.568 g = []
2025-07-02 05:48:08.576 if alo < ahi:
2025-07-02 05:48:08.589 if blo < bhi:
2025-07-02 05:48:08.601 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:08.613 else:
2025-07-02 05:48:08.622 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:08.632 elif blo < bhi:
2025-07-02 05:48:08.644 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:08.655
2025-07-02 05:48:08.666 >       yield from g
2025-07-02 05:48:08.675
2025-07-02 05:48:08.683 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:08.693 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:08.703
2025-07-02 05:48:08.713 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:08.727 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:08.739 alo = 172, ahi = 1101
2025-07-02 05:48:08.754 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:08.764 blo = 172, bhi = 1101
2025-07-02 05:48:08.772
2025-07-02 05:48:08.779 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:08.785 r"""
2025-07-02 05:48:08.791 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:08.799 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:08.809 synch point, and intraline difference marking is done on the
2025-07-02 05:48:08.817 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:08.826
2025-07-02 05:48:08.837 Example:
2025-07-02 05:48:08.847
2025-07-02 05:48:08.855 >>> d = Differ()
2025-07-02 05:48:08.865 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:08.877 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:08.885 >>> print(''.join(results), end="")
2025-07-02 05:48:08.893 - abcDefghiJkl
2025-07-02 05:48:08.907 + abcdefGhijkl
2025-07-02 05:48:08.926 """
2025-07-02 05:48:08.933
2025-07-02 05:48:08.941 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:08.947 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:08.953 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:08.965 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:08.976 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:08.986
2025-07-02 05:48:08.996 # search for the pair that matches best without being identical
2025-07-02 05:48:09.009 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:09.020 # on junk -- unless we have to)
2025-07-02 05:48:09.028 for j in range(blo, bhi):
2025-07-02 05:48:09.041 bj = b[j]
2025-07-02 05:48:09.048 cruncher.set_seq2(bj)
2025-07-02 05:48:09.055 for i in range(alo, ahi):
2025-07-02 05:48:09.061 ai = a[i]
2025-07-02 05:48:09.066 if ai == bj:
2025-07-02 05:48:09.073 if eqi is None:
2025-07-02 05:48:09.080 eqi, eqj = i, j
2025-07-02 05:48:09.088 continue
2025-07-02 05:48:09.096 cruncher.set_seq1(ai)
2025-07-02 05:48:09.103 # computing similarity is expensive, so use the quick
2025-07-02 05:48:09.110 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:09.119 # compares by a factor of 3.
2025-07-02 05:48:09.132 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:09.144 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:09.154 # of the computation is cached by cruncher
2025-07-02 05:48:09.161 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:09.168 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:09.176 cruncher.ratio() > best_ratio:
2025-07-02 05:48:09.183 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:09.191 if best_ratio < cutoff:
2025-07-02 05:48:09.198 # no non-identical "pretty close" pair
2025-07-02 05:48:09.205 if eqi is None:
2025-07-02 05:48:09.217 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:09.227 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:09.235 return
2025-07-02 05:48:09.242 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:09.247 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:09.253 else:
2025-07-02 05:48:09.259 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:09.265 eqi = None
2025-07-02 05:48:09.271
2025-07-02 05:48:09.278 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:09.285 # identical
2025-07-02 05:48:09.292
2025-07-02 05:48:09.300 # pump out diffs from before the synch point
2025-07-02 05:48:09.307 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:09.314
2025-07-02 05:48:09.320 # do intraline marking on the synch pair
2025-07-02 05:48:09.326 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:09.334 if eqi is None:
2025-07-02 05:48:09.340 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:09.346 atags = btags = ""
2025-07-02 05:48:09.353 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:09.360 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:09.366 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:09.373 if tag == 'replace':
2025-07-02 05:48:09.380 atags += '^' * la
2025-07-02 05:48:09.391 btags += '^' * lb
2025-07-02 05:48:09.403 elif tag == 'delete':
2025-07-02 05:48:09.410 atags += '-' * la
2025-07-02 05:48:09.417 elif tag == 'insert':
2025-07-02 05:48:09.423 btags += '+' * lb
2025-07-02 05:48:09.428 elif tag == 'equal':
2025-07-02 05:48:09.432 atags += ' ' * la
2025-07-02 05:48:09.437 btags += ' ' * lb
2025-07-02 05:48:09.442 else:
2025-07-02 05:48:09.449 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:09.455 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:09.461 else:
2025-07-02 05:48:09.468 # the synch pair is identical
2025-07-02 05:48:09.476 yield '  ' + aelt
2025-07-02 05:48:09.488
2025-07-02 05:48:09.498 # pump out diffs from after the synch point
2025-07-02 05:48:09.507 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:09.518
2025-07-02 05:48:09.526 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:09.535 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:09.546
2025-07-02 05:48:09.556 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:09.566 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:09.577 alo = 173, ahi = 1101
2025-07-02 05:48:09.590 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:09.601 blo = 173, bhi = 1101
2025-07-02 05:48:09.611
2025-07-02 05:48:09.625 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:09.638 g = []
2025-07-02 05:48:09.648 if alo < ahi:
2025-07-02 05:48:09.657 if blo < bhi:
2025-07-02 05:48:09.665 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:09.671 else:
2025-07-02 05:48:09.678 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:09.689 elif blo < bhi:
2025-07-02 05:48:09.698 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:09.707
2025-07-02 05:48:09.715 >       yield from g
2025-07-02 05:48:09.727
2025-07-02 05:48:09.736 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:09.744 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:09.751
2025-07-02 05:48:09.758 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:09.766 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:09.777 alo = 173, ahi = 1101
2025-07-02 05:48:09.789 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:09.800 blo = 173, bhi = 1101
2025-07-02 05:48:09.809
2025-07-02 05:48:09.819 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:09.832 r"""
2025-07-02 05:48:09.841 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:09.851 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:09.863 synch point, and intraline difference marking is done on the
2025-07-02 05:48:09.876 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:09.888
2025-07-02 05:48:09.899 Example:
2025-07-02 05:48:09.906
2025-07-02 05:48:09.914 >>> d = Differ()
2025-07-02 05:48:09.922 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:09.928 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:09.934 >>> print(''.join(results), end="")
2025-07-02 05:48:09.945 - abcDefghiJkl
2025-07-02 05:48:09.965 + abcdefGhijkl
2025-07-02 05:48:09.978 """
2025-07-02 05:48:09.989
2025-07-02 05:48:09.998 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:10.007 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:10.015 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:10.022 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:10.034 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:10.047
2025-07-02 05:48:10.057 # search for the pair that matches best without being identical
2025-07-02 05:48:10.065 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:10.072 # on junk -- unless we have to)
2025-07-02 05:48:10.077 for j in range(blo, bhi):
2025-07-02 05:48:10.082 bj = b[j]
2025-07-02 05:48:10.092 cruncher.set_seq2(bj)
2025-07-02 05:48:10.103 for i in range(alo, ahi):
2025-07-02 05:48:10.112 ai = a[i]
2025-07-02 05:48:10.120 if ai == bj:
2025-07-02 05:48:10.127 if eqi is None:
2025-07-02 05:48:10.133 eqi, eqj = i, j
2025-07-02 05:48:10.145 continue
2025-07-02 05:48:10.154 cruncher.set_seq1(ai)
2025-07-02 05:48:10.167 # computing similarity is expensive, so use the quick
2025-07-02 05:48:10.178 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:10.191 # compares by a factor of 3.
2025-07-02 05:48:10.200 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:10.209 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:10.216 # of the computation is cached by cruncher
2025-07-02 05:48:10.222 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:10.234 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:10.245 cruncher.ratio() > best_ratio:
2025-07-02 05:48:10.257 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:10.267 if best_ratio < cutoff:
2025-07-02 05:48:10.276 # no non-identical "pretty close" pair
2025-07-02 05:48:10.288 if eqi is None:
2025-07-02 05:48:10.298 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:10.311 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:10.323 return
2025-07-02 05:48:10.336 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:10.348 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:10.359 else:
2025-07-02 05:48:10.369 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:10.376 eqi = None
2025-07-02 05:48:10.385
2025-07-02 05:48:10.394 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:10.400 # identical
2025-07-02 05:48:10.407
2025-07-02 05:48:10.413 # pump out diffs from before the synch point
2025-07-02 05:48:10.419 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:10.427
2025-07-02 05:48:10.438 # do intraline marking on the synch pair
2025-07-02 05:48:10.447 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:10.455 if eqi is None:
2025-07-02 05:48:10.462 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:10.468 atags = btags = ""
2025-07-02 05:48:10.475 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:10.483 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:10.495 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:10.507 if tag == 'replace':
2025-07-02 05:48:10.516 atags += '^' * la
2025-07-02 05:48:10.522 btags += '^' * lb
2025-07-02 05:48:10.527 elif tag == 'delete':
2025-07-02 05:48:10.533 atags += '-' * la
2025-07-02 05:48:10.538 elif tag == 'insert':
2025-07-02 05:48:10.543 btags += '+' * lb
2025-07-02 05:48:10.553 elif tag == 'equal':
2025-07-02 05:48:10.567 atags += ' ' * la
2025-07-02 05:48:10.576 btags += ' ' * lb
2025-07-02 05:48:10.584 else:
2025-07-02 05:48:10.596 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:10.606 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:10.613 else:
2025-07-02 05:48:10.628 # the synch pair is identical
2025-07-02 05:48:10.636 yield '  ' + aelt
2025-07-02 05:48:10.643
2025-07-02 05:48:10.650 # pump out diffs from after the synch point
2025-07-02 05:48:10.655 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:10.661
2025-07-02 05:48:10.667 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:10.674 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:10.681
2025-07-02 05:48:10.688 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:10.695 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:10.702 alo = 174, ahi = 1101
2025-07-02 05:48:10.710 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:10.716 blo = 174, bhi = 1101
2025-07-02 05:48:10.729
2025-07-02 05:48:10.742 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:10.752 g = []
2025-07-02 05:48:10.761 if alo < ahi:
2025-07-02 05:48:10.769 if blo < bhi:
2025-07-02 05:48:10.776 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:10.782 else:
2025-07-02 05:48:10.793 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:10.803 elif blo < bhi:
2025-07-02 05:48:10.811 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:10.821
2025-07-02 05:48:10.829 >       yield from g
2025-07-02 05:48:10.835
2025-07-02 05:48:10.844 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:10.857 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:10.869
2025-07-02 05:48:10.876 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:10.882 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:10.887 alo = 174, ahi = 1101
2025-07-02 05:48:10.893 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:10.898 blo = 174, bhi = 1101
2025-07-02 05:48:10.908
2025-07-02 05:48:10.918 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:10.929 r"""
2025-07-02 05:48:10.935 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:10.942 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:10.954 synch point, and intraline difference marking is done on the
2025-07-02 05:48:10.963 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:10.971
2025-07-02 05:48:10.979 Example:
2025-07-02 05:48:10.991
2025-07-02 05:48:11.001 >>> d = Differ()
2025-07-02 05:48:11.016 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:11.027 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:11.039 >>> print(''.join(results), end="")
2025-07-02 05:48:11.051 - abcDefghiJkl
2025-07-02 05:48:11.067 + abcdefGhijkl
2025-07-02 05:48:11.083 """
2025-07-02 05:48:11.094
2025-07-02 05:48:11.103 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:11.110 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:11.116 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:11.123 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:11.134 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:11.147
2025-07-02 05:48:11.159 # search for the pair that matches best without being identical
2025-07-02 05:48:11.168 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:11.175 # on junk -- unless we have to)
2025-07-02 05:48:11.181 for j in range(blo, bhi):
2025-07-02 05:48:11.187 bj = b[j]
2025-07-02 05:48:11.192 cruncher.set_seq2(bj)
2025-07-02 05:48:11.197 for i in range(alo, ahi):
2025-07-02 05:48:11.202 ai = a[i]
2025-07-02 05:48:11.208 if ai == bj:
2025-07-02 05:48:11.214 if eqi is None:
2025-07-02 05:48:11.221 eqi, eqj = i, j
2025-07-02 05:48:11.226 continue
2025-07-02 05:48:11.232 cruncher.set_seq1(ai)
2025-07-02 05:48:11.247 # computing similarity is expensive, so use the quick
2025-07-02 05:48:11.260 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:11.272 # compares by a factor of 3.
2025-07-02 05:48:11.284 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:11.292 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:11.302 # of the computation is cached by cruncher
2025-07-02 05:48:11.312 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:11.319 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:11.326 cruncher.ratio() > best_ratio:
2025-07-02 05:48:11.332 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:11.337 if best_ratio < cutoff:
2025-07-02 05:48:11.342 # no non-identical "pretty close" pair
2025-07-02 05:48:11.347 if eqi is None:
2025-07-02 05:48:11.352 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:11.356 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:11.362 return
2025-07-02 05:48:11.367 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:11.373 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:11.378 else:
2025-07-02 05:48:11.389 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:11.399 eqi = None
2025-07-02 05:48:11.407
2025-07-02 05:48:11.415 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:11.423 # identical
2025-07-02 05:48:11.436
2025-07-02 05:48:11.446 # pump out diffs from before the synch point
2025-07-02 05:48:11.455 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:11.465
2025-07-02 05:48:11.472 # do intraline marking on the synch pair
2025-07-02 05:48:11.480 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:11.486 if eqi is None:
2025-07-02 05:48:11.493 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:11.499 atags = btags = ""
2025-07-02 05:48:11.505 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:11.517 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:11.525 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:11.532 if tag == 'replace':
2025-07-02 05:48:11.539 atags += '^' * la
2025-07-02 05:48:11.544 btags += '^' * lb
2025-07-02 05:48:11.550 elif tag == 'delete':
2025-07-02 05:48:11.564 atags += '-' * la
2025-07-02 05:48:11.576 elif tag == 'insert':
2025-07-02 05:48:11.588 btags += '+' * lb
2025-07-02 05:48:11.598 elif tag == 'equal':
2025-07-02 05:48:11.605 atags += ' ' * la
2025-07-02 05:48:11.610 btags += ' ' * lb
2025-07-02 05:48:11.615 else:
2025-07-02 05:48:11.621 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:11.627 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:11.637 else:
2025-07-02 05:48:11.647 # the synch pair is identical
2025-07-02 05:48:11.656 yield '  ' + aelt
2025-07-02 05:48:11.663
2025-07-02 05:48:11.671 # pump out diffs from after the synch point
2025-07-02 05:48:11.682 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:11.690
2025-07-02 05:48:11.699 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:11.706 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:11.712
2025-07-02 05:48:11.718 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:11.725 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:11.730 alo = 175, ahi = 1101
2025-07-02 05:48:11.737 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:11.750 blo = 175, bhi = 1101
2025-07-02 05:48:11.761
2025-07-02 05:48:11.771 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:11.782 g = []
2025-07-02 05:48:11.794 if alo < ahi:
2025-07-02 05:48:11.805 if blo < bhi:
2025-07-02 05:48:11.816 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:11.827 else:
2025-07-02 05:48:11.835 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:11.841 elif blo < bhi:
2025-07-02 05:48:11.846 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:11.851
2025-07-02 05:48:11.857 >       yield from g
2025-07-02 05:48:11.863
2025-07-02 05:48:11.870 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:11.881 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:11.892
2025-07-02 05:48:11.903 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:11.916 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:11.927 alo = 175, ahi = 1101
2025-07-02 05:48:11.941 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:11.952 blo = 175, bhi = 1101
2025-07-02 05:48:11.963
2025-07-02 05:48:11.972 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:11.979 r"""
2025-07-02 05:48:11.987 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:11.994 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:12.000 synch point, and intraline difference marking is done on the
2025-07-02 05:48:12.006 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:12.013
2025-07-02 05:48:12.026 Example:
2025-07-02 05:48:12.041
2025-07-02 05:48:12.054 >>> d = Differ()
2025-07-02 05:48:12.064 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:12.072 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:12.079 >>> print(''.join(results), end="")
2025-07-02 05:48:12.085 - abcDefghiJkl
2025-07-02 05:48:12.098 + abcdefGhijkl
2025-07-02 05:48:12.117 """
2025-07-02 05:48:12.124
2025-07-02 05:48:12.130 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:12.140 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:12.149 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:12.156 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:12.164 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:12.170
2025-07-02 05:48:12.179 # search for the pair that matches best without being identical
2025-07-02 05:48:12.187 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:12.195 # on junk -- unless we have to)
2025-07-02 05:48:12.202 for j in range(blo, bhi):
2025-07-02 05:48:12.209 bj = b[j]
2025-07-02 05:48:12.215 cruncher.set_seq2(bj)
2025-07-02 05:48:12.223 for i in range(alo, ahi):
2025-07-02 05:48:12.231 ai = a[i]
2025-07-02 05:48:12.239 if ai == bj:
2025-07-02 05:48:12.247 if eqi is None:
2025-07-02 05:48:12.254 eqi, eqj = i, j
2025-07-02 05:48:12.264 continue
2025-07-02 05:48:12.275 cruncher.set_seq1(ai)
2025-07-02 05:48:12.284 # computing similarity is expensive, so use the quick
2025-07-02 05:48:12.290 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:12.296 # compares by a factor of 3.
2025-07-02 05:48:12.301 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:12.314 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:12.325 # of the computation is cached by cruncher
2025-07-02 05:48:12.337 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:12.347 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:12.357 cruncher.ratio() > best_ratio:
2025-07-02 05:48:12.370 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:12.379 if best_ratio < cutoff:
2025-07-02 05:48:12.386 # no non-identical "pretty close" pair
2025-07-02 05:48:12.392 if eqi is None:
2025-07-02 05:48:12.398 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:12.406 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:12.417 return
2025-07-02 05:48:12.423 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:12.431 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:12.441 else:
2025-07-02 05:48:12.449 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:12.456 eqi = None
2025-07-02 05:48:12.462
2025-07-02 05:48:12.469 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:12.474 # identical
2025-07-02 05:48:12.480
2025-07-02 05:48:12.486 # pump out diffs from before the synch point
2025-07-02 05:48:12.497 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:12.504
2025-07-02 05:48:12.510 # do intraline marking on the synch pair
2025-07-02 05:48:12.516 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:12.521 if eqi is None:
2025-07-02 05:48:12.526 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:12.530 atags = btags = ""
2025-07-02 05:48:12.535 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:12.540 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:12.545 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:12.551 if tag == 'replace':
2025-07-02 05:48:12.557 atags += '^' * la
2025-07-02 05:48:12.563 btags += '^' * lb
2025-07-02 05:48:12.571 elif tag == 'delete':
2025-07-02 05:48:12.584 atags += '-' * la
2025-07-02 05:48:12.595 elif tag == 'insert':
2025-07-02 05:48:12.602 btags += '+' * lb
2025-07-02 05:48:12.613 elif tag == 'equal':
2025-07-02 05:48:12.625 atags += ' ' * la
2025-07-02 05:48:12.635 btags += ' ' * lb
2025-07-02 05:48:12.646 else:
2025-07-02 05:48:12.655 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:12.663 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:12.669 else:
2025-07-02 05:48:12.674 # the synch pair is identical
2025-07-02 05:48:12.679 yield '  ' + aelt
2025-07-02 05:48:12.685
2025-07-02 05:48:12.692 # pump out diffs from after the synch point
2025-07-02 05:48:12.698 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:12.705
2025-07-02 05:48:12.710 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:12.716 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:12.721
2025-07-02 05:48:12.728 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:12.734 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:12.740 alo = 176, ahi = 1101
2025-07-02 05:48:12.748 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:12.755 blo = 176, bhi = 1101
2025-07-02 05:48:12.762
2025-07-02 05:48:12.769 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:12.776 g = []
2025-07-02 05:48:12.784 if alo < ahi:
2025-07-02 05:48:12.795 if blo < bhi:
2025-07-02 05:48:12.802 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:12.808 else:
2025-07-02 05:48:12.813 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:12.818 elif blo < bhi:
2025-07-02 05:48:12.829 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:12.839
2025-07-02 05:48:12.847 >       yield from g
2025-07-02 05:48:12.854
2025-07-02 05:48:12.863 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:12.873 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:12.886
2025-07-02 05:48:12.895 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:12.905 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:12.912 alo = 176, ahi = 1101
2025-07-02 05:48:12.924 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:12.933 blo = 176, bhi = 1101
2025-07-02 05:48:12.941
2025-07-02 05:48:12.948 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:12.954 r"""
2025-07-02 05:48:12.960 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:12.967 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:12.974 synch point, and intraline difference marking is done on the
2025-07-02 05:48:12.980 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:12.987
2025-07-02 05:48:12.997 Example:
2025-07-02 05:48:13.005
2025-07-02 05:48:13.013 >>> d = Differ()
2025-07-02 05:48:13.020 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:13.032 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:13.041 >>> print(''.join(results), end="")
2025-07-02 05:48:13.053 - abcDefghiJkl
2025-07-02 05:48:13.075 + abcdefGhijkl
2025-07-02 05:48:13.094 """
2025-07-02 05:48:13.107
2025-07-02 05:48:13.119 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:13.128 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:13.135 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:13.143 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:13.154 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:13.163
2025-07-02 05:48:13.171 # search for the pair that matches best without being identical
2025-07-02 05:48:13.178 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:13.186 # on junk -- unless we have to)
2025-07-02 05:48:13.197 for j in range(blo, bhi):
2025-07-02 05:48:13.205 bj = b[j]
2025-07-02 05:48:13.216 cruncher.set_seq2(bj)
2025-07-02 05:48:13.226 for i in range(alo, ahi):
2025-07-02 05:48:13.236 ai = a[i]
2025-07-02 05:48:13.245 if ai == bj:
2025-07-02 05:48:13.258 if eqi is None:
2025-07-02 05:48:13.269 eqi, eqj = i, j
2025-07-02 05:48:13.277 continue
2025-07-02 05:48:13.291 cruncher.set_seq1(ai)
2025-07-02 05:48:13.297 # computing similarity is expensive, so use the quick
2025-07-02 05:48:13.302 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:13.308 # compares by a factor of 3.
2025-07-02 05:48:13.315 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:13.325 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:13.338 # of the computation is cached by cruncher
2025-07-02 05:48:13.349 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:13.358 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:13.365 cruncher.ratio() > best_ratio:
2025-07-02 05:48:13.371 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:13.376 if best_ratio < cutoff:
2025-07-02 05:48:13.382 # no non-identical "pretty close" pair
2025-07-02 05:48:13.387 if eqi is None:
2025-07-02 05:48:13.394 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:13.401 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:13.414 return
2025-07-02 05:48:13.425 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:13.432 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:13.443 else:
2025-07-02 05:48:13.455 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:13.464 eqi = None
2025-07-02 05:48:13.472
2025-07-02 05:48:13.479 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:13.484 # identical
2025-07-02 05:48:13.488
2025-07-02 05:48:13.494 # pump out diffs from before the synch point
2025-07-02 05:48:13.499 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:13.503
2025-07-02 05:48:13.509 # do intraline marking on the synch pair
2025-07-02 05:48:13.515 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:13.520 if eqi is None:
2025-07-02 05:48:13.526 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:13.539 atags = btags = ""
2025-07-02 05:48:13.549 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:13.559 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:13.571 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:13.583 if tag == 'replace':
2025-07-02 05:48:13.594 atags += '^' * la
2025-07-02 05:48:13.607 btags += '^' * lb
2025-07-02 05:48:13.619 elif tag == 'delete':
2025-07-02 05:48:13.633 atags += '-' * la
2025-07-02 05:48:13.645 elif tag == 'insert':
2025-07-02 05:48:13.656 btags += '+' * lb
2025-07-02 05:48:13.665 elif tag == 'equal':
2025-07-02 05:48:13.673 atags += ' ' * la
2025-07-02 05:48:13.681 btags += ' ' * lb
2025-07-02 05:48:13.687 else:
2025-07-02 05:48:13.694 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:13.700 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:13.706 else:
2025-07-02 05:48:13.716 # the synch pair is identical
2025-07-02 05:48:13.728 yield '  ' + aelt
2025-07-02 05:48:13.739
2025-07-02 05:48:13.749 # pump out diffs from after the synch point
2025-07-02 05:48:13.759 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:13.767
2025-07-02 05:48:13.774 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:13.780 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:13.786
2025-07-02 05:48:13.799 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:13.812 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:13.820 alo = 177, ahi = 1101
2025-07-02 05:48:13.834 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:13.846 blo = 177, bhi = 1101
2025-07-02 05:48:13.857
2025-07-02 05:48:13.870 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:13.880 g = []
2025-07-02 05:48:13.888 if alo < ahi:
2025-07-02 05:48:13.895 if blo < bhi:
2025-07-02 05:48:13.902 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:13.915 else:
2025-07-02 05:48:13.927 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:13.935 elif blo < bhi:
2025-07-02 05:48:13.941 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:13.947
2025-07-02 05:48:13.955 >       yield from g
2025-07-02 05:48:13.966
2025-07-02 05:48:13.973 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:13.981 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:13.988
2025-07-02 05:48:13.993 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:14.000 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:14.013 alo = 177, ahi = 1101
2025-07-02 05:48:14.021 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:14.028 blo = 177, bhi = 1101
2025-07-02 05:48:14.034
2025-07-02 05:48:14.041 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:14.048 r"""
2025-07-02 05:48:14.056 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:14.064 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:14.070 synch point, and intraline difference marking is done on the
2025-07-02 05:48:14.076 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:14.083
2025-07-02 05:48:14.092 Example:
2025-07-02 05:48:14.102
2025-07-02 05:48:14.112 >>> d = Differ()
2025-07-02 05:48:14.120 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:14.128 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:14.139 >>> print(''.join(results), end="")
2025-07-02 05:48:14.151 - abcDefghiJkl
2025-07-02 05:48:14.171 + abcdefGhijkl
2025-07-02 05:48:14.191 """
2025-07-02 05:48:14.197
2025-07-02 05:48:14.203 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:14.208 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:14.213 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:14.228 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:14.239 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:14.250
2025-07-02 05:48:14.259 # search for the pair that matches best without being identical
2025-07-02 05:48:14.267 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:14.276 # on junk -- unless we have to)
2025-07-02 05:48:14.292 for j in range(blo, bhi):
2025-07-02 05:48:14.301 bj = b[j]
2025-07-02 05:48:14.309 cruncher.set_seq2(bj)
2025-07-02 05:48:14.315 for i in range(alo, ahi):
2025-07-02 05:48:14.321 ai = a[i]
2025-07-02 05:48:14.327 if ai == bj:
2025-07-02 05:48:14.332 if eqi is None:
2025-07-02 05:48:14.336 eqi, eqj = i, j
2025-07-02 05:48:14.341 continue
2025-07-02 05:48:14.347 cruncher.set_seq1(ai)
2025-07-02 05:48:14.353 # computing similarity is expensive, so use the quick
2025-07-02 05:48:14.358 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:14.363 # compares by a factor of 3.
2025-07-02 05:48:14.368 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:14.374 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:14.381 # of the computation is cached by cruncher
2025-07-02 05:48:14.388 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:14.399 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:14.407 cruncher.ratio() > best_ratio:
2025-07-02 05:48:14.417 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:14.426 if best_ratio < cutoff:
2025-07-02 05:48:14.433 # no non-identical "pretty close" pair
2025-07-02 05:48:14.445 if eqi is None:
2025-07-02 05:48:14.454 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:14.462 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:14.474 return
2025-07-02 05:48:14.483 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:14.492 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:14.500 else:
2025-07-02 05:48:14.507 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:14.515 eqi = None
2025-07-02 05:48:14.522
2025-07-02 05:48:14.531 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:14.542 # identical
2025-07-02 05:48:14.551
2025-07-02 05:48:14.558 # pump out diffs from before the synch point
2025-07-02 05:48:14.570 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:14.578
2025-07-02 05:48:14.587 # do intraline marking on the synch pair
2025-07-02 05:48:14.594 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:14.603 if eqi is None:
2025-07-02 05:48:14.615 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:14.624 atags = btags = ""
2025-07-02 05:48:14.634 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:14.647 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:14.661 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:14.675 if tag == 'replace':
2025-07-02 05:48:14.685 atags += '^' * la
2025-07-02 05:48:14.692 btags += '^' * lb
2025-07-02 05:48:14.702 elif tag == 'delete':
2025-07-02 05:48:14.710 atags += '-' * la
2025-07-02 05:48:14.720 elif tag == 'insert':
2025-07-02 05:48:14.729 btags += '+' * lb
2025-07-02 05:48:14.740 elif tag == 'equal':
2025-07-02 05:48:14.752 atags += ' ' * la
2025-07-02 05:48:14.763 btags += ' ' * lb
2025-07-02 05:48:14.772 else:
2025-07-02 05:48:14.782 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:14.796 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:14.807 else:
2025-07-02 05:48:14.816 # the synch pair is identical
2025-07-02 05:48:14.824 yield '  ' + aelt
2025-07-02 05:48:14.830
2025-07-02 05:48:14.837 # pump out diffs from after the synch point
2025-07-02 05:48:14.849 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:14.860
2025-07-02 05:48:14.867 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:14.875 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:14.880
2025-07-02 05:48:14.887 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:14.894 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:14.900 alo = 180, ahi = 1101
2025-07-02 05:48:14.911 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:14.925 blo = 180, bhi = 1101
2025-07-02 05:48:14.936
2025-07-02 05:48:14.944 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:14.951 g = []
2025-07-02 05:48:14.964 if alo < ahi:
2025-07-02 05:48:14.975 if blo < bhi:
2025-07-02 05:48:14.985 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:14.992 else:
2025-07-02 05:48:14.999 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:15.005 elif blo < bhi:
2025-07-02 05:48:15.010 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:15.015
2025-07-02 05:48:15.022 >       yield from g
2025-07-02 05:48:15.035
2025-07-02 05:48:15.043 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:15.050 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:15.058
2025-07-02 05:48:15.067 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:15.080 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:15.090 alo = 180, ahi = 1101
2025-07-02 05:48:15.099 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:15.105 blo = 180, bhi = 1101
2025-07-02 05:48:15.112
2025-07-02 05:48:15.127 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:15.136 r"""
2025-07-02 05:48:15.145 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:15.154 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:15.166 synch point, and intraline difference marking is done on the
2025-07-02 05:48:15.176 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:15.184
2025-07-02 05:48:15.198 Example:
2025-07-02 05:48:15.205
2025-07-02 05:48:15.212 >>> d = Differ()
2025-07-02 05:48:15.219 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:15.227 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:15.240 >>> print(''.join(results), end="")
2025-07-02 05:48:15.250 - abcDefghiJkl
2025-07-02 05:48:15.272 + abcdefGhijkl
2025-07-02 05:48:15.289 """
2025-07-02 05:48:15.295
2025-07-02 05:48:15.302 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:15.311 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:15.323 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:15.333 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:15.345 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:15.359
2025-07-02 05:48:15.370 # search for the pair that matches best without being identical
2025-07-02 05:48:15.379 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:15.392 # on junk -- unless we have to)
2025-07-02 05:48:15.402 for j in range(blo, bhi):
2025-07-02 05:48:15.413 bj = b[j]
2025-07-02 05:48:15.422 cruncher.set_seq2(bj)
2025-07-02 05:48:15.433 for i in range(alo, ahi):
2025-07-02 05:48:15.443 ai = a[i]
2025-07-02 05:48:15.450 if ai == bj:
2025-07-02 05:48:15.462 if eqi is None:
2025-07-02 05:48:15.475 eqi, eqj = i, j
2025-07-02 05:48:15.487 continue
2025-07-02 05:48:15.500 cruncher.set_seq1(ai)
2025-07-02 05:48:15.509 # computing similarity is expensive, so use the quick
2025-07-02 05:48:15.515 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:15.521 # compares by a factor of 3.
2025-07-02 05:48:15.527 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:15.534 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:15.540 # of the computation is cached by cruncher
2025-07-02 05:48:15.547 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:15.555 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:15.565 cruncher.ratio() > best_ratio:
2025-07-02 05:48:15.573 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:15.581 if best_ratio < cutoff:
2025-07-02 05:48:15.588 # no non-identical "pretty close" pair
2025-07-02 05:48:15.597 if eqi is None:
2025-07-02 05:48:15.605 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:15.614 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:15.621 return
2025-07-02 05:48:15.627 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:15.640 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:15.651 else:
2025-07-02 05:48:15.660 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:15.673 eqi = None
2025-07-02 05:48:15.682
2025-07-02 05:48:15.690 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:15.696 # identical
2025-07-02 05:48:15.701
2025-07-02 05:48:15.709 # pump out diffs from before the synch point
2025-07-02 05:48:15.718 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:15.725
2025-07-02 05:48:15.731 # do intraline marking on the synch pair
2025-07-02 05:48:15.737 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:15.743 if eqi is None:
2025-07-02 05:48:15.749 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:15.754 atags = btags = ""
2025-07-02 05:48:15.760 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:15.766 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:15.772 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:15.778 if tag == 'replace':
2025-07-02 05:48:15.784 atags += '^' * la
2025-07-02 05:48:15.789 btags += '^' * lb
2025-07-02 05:48:15.796 elif tag == 'delete':
2025-07-02 05:48:15.804 atags += '-' * la
2025-07-02 05:48:15.811 elif tag == 'insert':
2025-07-02 05:48:15.818 btags += '+' * lb
2025-07-02 05:48:15.825 elif tag == 'equal':
2025-07-02 05:48:15.836 atags += ' ' * la
2025-07-02 05:48:15.848 btags += ' ' * lb
2025-07-02 05:48:15.859 else:
2025-07-02 05:48:15.866 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:15.877 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:15.887 else:
2025-07-02 05:48:15.894 # the synch pair is identical
2025-07-02 05:48:15.900 yield '  ' + aelt
2025-07-02 05:48:15.906
2025-07-02 05:48:15.913 # pump out diffs from after the synch point
2025-07-02 05:48:15.920 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:15.927
2025-07-02 05:48:15.933 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:15.944 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:15.955
2025-07-02 05:48:15.965 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:15.978 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:15.985 alo = 181, ahi = 1101
2025-07-02 05:48:15.996 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:16.007 blo = 181, bhi = 1101
2025-07-02 05:48:16.018
2025-07-02 05:48:16.025 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:16.034 g = []
2025-07-02 05:48:16.043 if alo < ahi:
2025-07-02 05:48:16.050 if blo < bhi:
2025-07-02 05:48:16.057 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:16.070 else:
2025-07-02 05:48:16.083 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:16.093 elif blo < bhi:
2025-07-02 05:48:16.104 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:16.115
2025-07-02 05:48:16.128 >       yield from g
2025-07-02 05:48:16.139
2025-07-02 05:48:16.150 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:16.163 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:16.174
2025-07-02 05:48:16.184 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:16.197 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:16.206 alo = 181, ahi = 1101
2025-07-02 05:48:16.214 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:16.220 blo = 181, bhi = 1101
2025-07-02 05:48:16.225
2025-07-02 05:48:16.230 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:16.241 r"""
2025-07-02 05:48:16.250 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:16.258 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:16.266 synch point, and intraline difference marking is done on the
2025-07-02 05:48:16.275 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:16.283
2025-07-02 05:48:16.290 Example:
2025-07-02 05:48:16.300
2025-07-02 05:48:16.309 >>> d = Differ()
2025-07-02 05:48:16.316 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:16.323 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:16.334 >>> print(''.join(results), end="")
2025-07-02 05:48:16.346 - abcDefghiJkl
2025-07-02 05:48:16.369 + abcdefGhijkl
2025-07-02 05:48:16.387 """
2025-07-02 05:48:16.400
2025-07-02 05:48:16.412 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:16.422 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:16.432 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:16.441 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:16.452 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:16.462
2025-07-02 05:48:16.469 # search for the pair that matches best without being identical
2025-07-02 05:48:16.476 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:16.481 # on junk -- unless we have to)
2025-07-02 05:48:16.487 for j in range(blo, bhi):
2025-07-02 05:48:16.491 bj = b[j]
2025-07-02 05:48:16.496 cruncher.set_seq2(bj)
2025-07-02 05:48:16.501 for i in range(alo, ahi):
2025-07-02 05:48:16.506 ai = a[i]
2025-07-02 05:48:16.512 if ai == bj:
2025-07-02 05:48:16.520 if eqi is None:
2025-07-02 05:48:16.527 eqi, eqj = i, j
2025-07-02 05:48:16.534 continue
2025-07-02 05:48:16.544 cruncher.set_seq1(ai)
2025-07-02 05:48:16.555 # computing similarity is expensive, so use the quick
2025-07-02 05:48:16.564 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:16.571 # compares by a factor of 3.
2025-07-02 05:48:16.577 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:16.584 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:16.590 # of the computation is cached by cruncher
2025-07-02 05:48:16.599 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:16.606 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:16.613 cruncher.ratio() > best_ratio:
2025-07-02 05:48:16.620 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:16.627 if best_ratio < cutoff:
2025-07-02 05:48:16.634 # no non-identical "pretty close" pair
2025-07-02 05:48:16.640 if eqi is None:
2025-07-02 05:48:16.650 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:16.661 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:16.669 return
2025-07-02 05:48:16.676 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:16.682 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:16.688 else:
2025-07-02 05:48:16.695 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:16.701 eqi = None
2025-07-02 05:48:16.707
2025-07-02 05:48:16.717 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:16.727 # identical
2025-07-02 05:48:16.734
2025-07-02 05:48:16.741 # pump out diffs from before the synch point
2025-07-02 05:48:16.749 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:16.754
2025-07-02 05:48:16.759 # do intraline marking on the synch pair
2025-07-02 05:48:16.764 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:16.769 if eqi is None:
2025-07-02 05:48:16.774 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:16.789 atags = btags = ""
2025-07-02 05:48:16.798 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:16.807 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:16.819 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:16.828 if tag == 'replace':
2025-07-02 05:48:16.839 atags += '^' * la
2025-07-02 05:48:16.847 btags += '^' * lb
2025-07-02 05:48:16.853 elif tag == 'delete':
2025-07-02 05:48:16.859 atags += '-' * la
2025-07-02 05:48:16.866 elif tag == 'insert':
2025-07-02 05:48:16.876 btags += '+' * lb
2025-07-02 05:48:16.885 elif tag == 'equal':
2025-07-02 05:48:16.893 atags += ' ' * la
2025-07-02 05:48:16.900 btags += ' ' * lb
2025-07-02 05:48:16.908 else:
2025-07-02 05:48:16.917 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:16.924 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:16.931 else:
2025-07-02 05:48:16.939 # the synch pair is identical
2025-07-02 05:48:16.951 yield '  ' + aelt
2025-07-02 05:48:16.959
2025-07-02 05:48:16.966 # pump out diffs from after the synch point
2025-07-02 05:48:16.973 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:16.979
2025-07-02 05:48:16.985 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:16.992 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:17.001
2025-07-02 05:48:17.013 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:17.024 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:17.031 alo = 182, ahi = 1101
2025-07-02 05:48:17.040 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:17.046 blo = 182, bhi = 1101
2025-07-02 05:48:17.052
2025-07-02 05:48:17.058 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:17.064 g = []
2025-07-02 05:48:17.071 if alo < ahi:
2025-07-02 05:48:17.084 if blo < bhi:
2025-07-02 05:48:17.097 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:17.106 else:
2025-07-02 05:48:17.113 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:17.120 elif blo < bhi:
2025-07-02 05:48:17.128 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:17.135
2025-07-02 05:48:17.145 >       yield from g
2025-07-02 05:48:17.155
2025-07-02 05:48:17.163 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:17.172 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:17.181
2025-07-02 05:48:17.189 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:17.198 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:17.203 alo = 182, ahi = 1101
2025-07-02 05:48:17.210 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:17.215 blo = 182, bhi = 1101
2025-07-02 05:48:17.221
2025-07-02 05:48:17.227 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:17.235 r"""
2025-07-02 05:48:17.247 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:17.258 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:17.270 synch point, and intraline difference marking is done on the
2025-07-02 05:48:17.284 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:17.298
2025-07-02 05:48:17.309 Example:
2025-07-02 05:48:17.319
2025-07-02 05:48:17.329 >>> d = Differ()
2025-07-02 05:48:17.339 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:17.349 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:17.360 >>> print(''.join(results), end="")
2025-07-02 05:48:17.372 - abcDefghiJkl
2025-07-02 05:48:17.393 + abcdefGhijkl
2025-07-02 05:48:17.413 """
2025-07-02 05:48:17.426
2025-07-02 05:48:17.439 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:17.446 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:17.451 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:17.457 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:17.462 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:17.471
2025-07-02 05:48:17.479 # search for the pair that matches best without being identical
2025-07-02 05:48:17.488 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:17.499 # on junk -- unless we have to)
2025-07-02 05:48:17.512 for j in range(blo, bhi):
2025-07-02 05:48:17.523 bj = b[j]
2025-07-02 05:48:17.534 cruncher.set_seq2(bj)
2025-07-02 05:48:17.542 for i in range(alo, ahi):
2025-07-02 05:48:17.552 ai = a[i]
2025-07-02 05:48:17.564 if ai == bj:
2025-07-02 05:48:17.573 if eqi is None:
2025-07-02 05:48:17.580 eqi, eqj = i, j
2025-07-02 05:48:17.587 continue
2025-07-02 05:48:17.594 cruncher.set_seq1(ai)
2025-07-02 05:48:17.601 # computing similarity is expensive, so use the quick
2025-07-02 05:48:17.608 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:17.615 # compares by a factor of 3.
2025-07-02 05:48:17.625 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:17.636 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:17.645 # of the computation is cached by cruncher
2025-07-02 05:48:17.657 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:17.671 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:17.681 cruncher.ratio() > best_ratio:
2025-07-02 05:48:17.692 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:17.704 if best_ratio < cutoff:
2025-07-02 05:48:17.713 # no non-identical "pretty close" pair
2025-07-02 05:48:17.722 if eqi is None:
2025-07-02 05:48:17.733 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:17.742 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:17.748 return
2025-07-02 05:48:17.755 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:17.761 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:17.767 else:
2025-07-02 05:48:17.774 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:17.780 eqi = None
2025-07-02 05:48:17.787
2025-07-02 05:48:17.793 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:17.799 # identical
2025-07-02 05:48:17.804
2025-07-02 05:48:17.813 # pump out diffs from before the synch point
2025-07-02 05:48:17.822 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:17.830
2025-07-02 05:48:17.837 # do intraline marking on the synch pair
2025-07-02 05:48:17.845 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:17.852 if eqi is None:
2025-07-02 05:48:17.859 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:17.868 atags = btags = ""
2025-07-02 05:48:17.881 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:17.890 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:17.897 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:17.905 if tag == 'replace':
2025-07-02 05:48:17.911 atags += '^' * la
2025-07-02 05:48:17.917 btags += '^' * lb
2025-07-02 05:48:17.922 elif tag == 'delete':
2025-07-02 05:48:17.929 atags += '-' * la
2025-07-02 05:48:17.943 elif tag == 'insert':
2025-07-02 05:48:17.953 btags += '+' * lb
2025-07-02 05:48:17.961 elif tag == 'equal':
2025-07-02 05:48:17.967 atags += ' ' * la
2025-07-02 05:48:17.973 btags += ' ' * lb
2025-07-02 05:48:17.979 else:
2025-07-02 05:48:17.985 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:17.990 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:18.002 else:
2025-07-02 05:48:18.014 # the synch pair is identical
2025-07-02 05:48:18.026 yield '  ' + aelt
2025-07-02 05:48:18.037
2025-07-02 05:48:18.045 # pump out diffs from after the synch point
2025-07-02 05:48:18.057 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:18.065
2025-07-02 05:48:18.072 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:18.080 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:18.088
2025-07-02 05:48:18.096 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:18.108 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:18.118 alo = 183, ahi = 1101
2025-07-02 05:48:18.131 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:18.142 blo = 183, bhi = 1101
2025-07-02 05:48:18.153
2025-07-02 05:48:18.164 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:18.173 g = []
2025-07-02 05:48:18.181 if alo < ahi:
2025-07-02 05:48:18.188 if blo < bhi:
2025-07-02 05:48:18.195 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:18.202 else:
2025-07-02 05:48:18.208 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:18.221 elif blo < bhi:
2025-07-02 05:48:18.234 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:18.244
2025-07-02 05:48:18.255 >       yield from g
2025-07-02 05:48:18.266
2025-07-02 05:48:18.276 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:18.289 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:18.299
2025-07-02 05:48:18.307 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:18.315 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:18.322 alo = 183, ahi = 1101
2025-07-02 05:48:18.330 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:18.335 blo = 183, bhi = 1101
2025-07-02 05:48:18.339
2025-07-02 05:48:18.344 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:18.349 r"""
2025-07-02 05:48:18.354 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:18.363 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:18.372 synch point, and intraline difference marking is done on the
2025-07-02 05:48:18.378 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:18.387
2025-07-02 05:48:18.397 Example:
2025-07-02 05:48:18.404
2025-07-02 05:48:18.411 >>> d = Differ()
2025-07-02 05:48:18.419 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:18.428 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:18.436 >>> print(''.join(results), end="")
2025-07-02 05:48:18.444 - abcDefghiJkl
2025-07-02 05:48:18.456 + abcdefGhijkl
2025-07-02 05:48:18.469 """
2025-07-02 05:48:18.475
2025-07-02 05:48:18.483 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:18.495 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:18.503 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:18.510 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:18.516 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:18.521
2025-07-02 05:48:18.526 # search for the pair that matches best without being identical
2025-07-02 05:48:18.531 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:18.537 # on junk -- unless we have to)
2025-07-02 05:48:18.543 for j in range(blo, bhi):
2025-07-02 05:48:18.549 bj = b[j]
2025-07-02 05:48:18.555 cruncher.set_seq2(bj)
2025-07-02 05:48:18.563 for i in range(alo, ahi):
2025-07-02 05:48:18.573 ai = a[i]
2025-07-02 05:48:18.583 if ai == bj:
2025-07-02 05:48:18.594 if eqi is None:
2025-07-02 05:48:18.602 eqi, eqj = i, j
2025-07-02 05:48:18.609 continue
2025-07-02 05:48:18.615 cruncher.set_seq1(ai)
2025-07-02 05:48:18.622 # computing similarity is expensive, so use the quick
2025-07-02 05:48:18.632 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:18.641 # compares by a factor of 3.
2025-07-02 05:48:18.648 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:18.654 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:18.660 # of the computation is cached by cruncher
2025-07-02 05:48:18.666 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:18.672 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:18.679 cruncher.ratio() > best_ratio:
2025-07-02 05:48:18.686 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:18.694 if best_ratio < cutoff:
2025-07-02 05:48:18.706 # no non-identical "pretty close" pair
2025-07-02 05:48:18.714 if eqi is None:
2025-07-02 05:48:18.721 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:18.729 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:18.736 return
2025-07-02 05:48:18.744 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:18.751 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:18.759 else:
2025-07-02 05:48:18.767 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:18.774 eqi = None
2025-07-02 05:48:18.783
2025-07-02 05:48:18.792 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:18.799 # identical
2025-07-02 05:48:18.806
2025-07-02 05:48:18.814 # pump out diffs from before the synch point
2025-07-02 05:48:18.827 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:18.835
2025-07-02 05:48:18.842 # do intraline marking on the synch pair
2025-07-02 05:48:18.848 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:18.854 if eqi is None:
2025-07-02 05:48:18.861 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:18.867 atags = btags = ""
2025-07-02 05:48:18.874 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:18.883 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:18.894 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:18.902 if tag == 'replace':
2025-07-02 05:48:18.909 atags += '^' * la
2025-07-02 05:48:18.915 btags += '^' * lb
2025-07-02 05:48:18.921 elif tag == 'delete':
2025-07-02 05:48:18.926 atags += '-' * la
2025-07-02 05:48:18.932 elif tag == 'insert':
2025-07-02 05:48:18.938 btags += '+' * lb
2025-07-02 05:48:18.945 elif tag == 'equal':
2025-07-02 05:48:18.952 atags += ' ' * la
2025-07-02 05:48:18.959 btags += ' ' * lb
2025-07-02 05:48:18.967 else:
2025-07-02 05:48:18.975 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:18.983 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:18.991 else:
2025-07-02 05:48:19.002 # the synch pair is identical
2025-07-02 05:48:19.012 yield '  ' + aelt
2025-07-02 05:48:19.019
2025-07-02 05:48:19.024 # pump out diffs from after the synch point
2025-07-02 05:48:19.030 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:19.036
2025-07-02 05:48:19.041 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:19.046 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:19.050
2025-07-02 05:48:19.055 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:19.061 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:19.068 alo = 184, ahi = 1101
2025-07-02 05:48:19.075 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:19.088 blo = 184, bhi = 1101
2025-07-02 05:48:19.100
2025-07-02 05:48:19.108 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:19.114 g = []
2025-07-02 05:48:19.119 if alo < ahi:
2025-07-02 05:48:19.125 if blo < bhi:
2025-07-02 05:48:19.131 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:19.137 else:
2025-07-02 05:48:19.144 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:19.152 elif blo < bhi:
2025-07-02 05:48:19.159 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:19.168
2025-07-02 05:48:19.176 >       yield from g
2025-07-02 05:48:19.183
2025-07-02 05:48:19.190 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:19.198 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:19.205
2025-07-02 05:48:19.212 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:19.220 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:19.229 alo = 184, ahi = 1101
2025-07-02 05:48:19.243 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:19.253 blo = 184, bhi = 1101
2025-07-02 05:48:19.266
2025-07-02 05:48:19.276 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:19.287 r"""
2025-07-02 05:48:19.298 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:19.309 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:19.321 synch point, and intraline difference marking is done on the
2025-07-02 05:48:19.332 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:19.343
2025-07-02 05:48:19.355 Example:
2025-07-02 05:48:19.365
2025-07-02 05:48:19.373 >>> d = Differ()
2025-07-02 05:48:19.380 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:19.387 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:19.395 >>> print(''.join(results), end="")
2025-07-02 05:48:19.406 - abcDefghiJkl
2025-07-02 05:48:19.422 + abcdefGhijkl
2025-07-02 05:48:19.439 """
2025-07-02 05:48:19.446
2025-07-02 05:48:19.455 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:19.466 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:19.476 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:19.485 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:19.492 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:19.508
2025-07-02 05:48:19.519 # search for the pair that matches best without being identical
2025-07-02 05:48:19.529 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:19.541 # on junk -- unless we have to)
2025-07-02 05:48:19.553 for j in range(blo, bhi):
2025-07-02 05:48:19.564 bj = b[j]
2025-07-02 05:48:19.578 cruncher.set_seq2(bj)
2025-07-02 05:48:19.590 for i in range(alo, ahi):
2025-07-02 05:48:19.601 ai = a[i]
2025-07-02 05:48:19.612 if ai == bj:
2025-07-02 05:48:19.626 if eqi is None:
2025-07-02 05:48:19.636 eqi, eqj = i, j
2025-07-02 05:48:19.643 continue
2025-07-02 05:48:19.650 cruncher.set_seq1(ai)
2025-07-02 05:48:19.659 # computing similarity is expensive, so use the quick
2025-07-02 05:48:19.669 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:19.679 # compares by a factor of 3.
2025-07-02 05:48:19.690 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:19.702 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:19.713 # of the computation is cached by cruncher
2025-07-02 05:48:19.720 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:19.728 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:19.734 cruncher.ratio() > best_ratio:
2025-07-02 05:48:19.741 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:19.748 if best_ratio < cutoff:
2025-07-02 05:48:19.756 # no non-identical "pretty close" pair
2025-07-02 05:48:19.766 if eqi is None:
2025-07-02 05:48:19.776 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:19.786 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:19.791 return
2025-07-02 05:48:19.796 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:19.801 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:19.807 else:
2025-07-02 05:48:19.814 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:19.820 eqi = None
2025-07-02 05:48:19.826
2025-07-02 05:48:19.834 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:19.843 # identical
2025-07-02 05:48:19.850
2025-07-02 05:48:19.857 # pump out diffs from before the synch point
2025-07-02 05:48:19.864 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:19.875
2025-07-02 05:48:19.885 # do intraline marking on the synch pair
2025-07-02 05:48:19.898 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:19.907 if eqi is None:
2025-07-02 05:48:19.915 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:19.923 atags = btags = ""
2025-07-02 05:48:19.930 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:19.937 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:19.950 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:19.961 if tag == 'replace':
2025-07-02 05:48:19.969 atags += '^' * la
2025-07-02 05:48:19.977 btags += '^' * lb
2025-07-02 05:48:19.984 elif tag == 'delete':
2025-07-02 05:48:19.990 atags += '-' * la
2025-07-02 05:48:19.997 elif tag == 'insert':
2025-07-02 05:48:20.002 btags += '+' * lb
2025-07-02 05:48:20.009 elif tag == 'equal':
2025-07-02 05:48:20.015 atags += ' ' * la
2025-07-02 05:48:20.021 btags += ' ' * lb
2025-07-02 05:48:20.026 else:
2025-07-02 05:48:20.031 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:20.037 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:20.043 else:
2025-07-02 05:48:20.052 # the synch pair is identical
2025-07-02 05:48:20.068 yield '  ' + aelt
2025-07-02 05:48:20.077
2025-07-02 05:48:20.084 # pump out diffs from after the synch point
2025-07-02 05:48:20.091 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:20.097
2025-07-02 05:48:20.102 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:20.107 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:20.112
2025-07-02 05:48:20.117 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:20.126 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:20.134 alo = 185, ahi = 1101
2025-07-02 05:48:20.143 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:20.152 blo = 185, bhi = 1101
2025-07-02 05:48:20.161
2025-07-02 05:48:20.174 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:20.186 g = []
2025-07-02 05:48:20.195 if alo < ahi:
2025-07-02 05:48:20.205 if blo < bhi:
2025-07-02 05:48:20.213 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:20.220 else:
2025-07-02 05:48:20.227 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:20.235 elif blo < bhi:
2025-07-02 05:48:20.246 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:20.255
2025-07-02 05:48:20.266 >       yield from g
2025-07-02 05:48:20.276
2025-07-02 05:48:20.285 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:20.293 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:20.299
2025-07-02 05:48:20.306 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:20.320 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:20.332 alo = 185, ahi = 1101
2025-07-02 05:48:20.341 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:20.352 blo = 185, bhi = 1101
2025-07-02 05:48:20.365
2025-07-02 05:48:20.377 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:20.387 r"""
2025-07-02 05:48:20.396 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:20.410 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:20.422 synch point, and intraline difference marking is done on the
2025-07-02 05:48:20.434 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:20.443
2025-07-02 05:48:20.455 Example:
2025-07-02 05:48:20.464
2025-07-02 05:48:20.472 >>> d = Differ()
2025-07-02 05:48:20.480 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:20.487 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:20.496 >>> print(''.join(results), end="")
2025-07-02 05:48:20.507 - abcDefghiJkl
2025-07-02 05:48:20.525 + abcdefGhijkl
2025-07-02 05:48:20.545 """
2025-07-02 05:48:20.554
2025-07-02 05:48:20.562 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:20.575 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:20.585 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:20.599 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:20.610 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:20.622
2025-07-02 05:48:20.635 # search for the pair that matches best without being identical
2025-07-02 05:48:20.649 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:20.658 # on junk -- unless we have to)
2025-07-02 05:48:20.669 for j in range(blo, bhi):
2025-07-02 05:48:20.679 bj = b[j]
2025-07-02 05:48:20.691 cruncher.set_seq2(bj)
2025-07-02 05:48:20.700 for i in range(alo, ahi):
2025-07-02 05:48:20.708 ai = a[i]
2025-07-02 05:48:20.714 if ai == bj:
2025-07-02 05:48:20.719 if eqi is None:
2025-07-02 05:48:20.726 eqi, eqj = i, j
2025-07-02 05:48:20.734 continue
2025-07-02 05:48:20.743 cruncher.set_seq1(ai)
2025-07-02 05:48:20.754 # computing similarity is expensive, so use the quick
2025-07-02 05:48:20.763 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:20.769 # compares by a factor of 3.
2025-07-02 05:48:20.776 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:20.789 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:20.799 # of the computation is cached by cruncher
2025-07-02 05:48:20.807 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:20.815 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:20.823 cruncher.ratio() > best_ratio:
2025-07-02 05:48:20.831 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:20.840 if best_ratio < cutoff:
2025-07-02 05:48:20.849 # no non-identical "pretty close" pair
2025-07-02 05:48:20.856 if eqi is None:
2025-07-02 05:48:20.861 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:20.866 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:20.872 return
2025-07-02 05:48:20.878 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:20.885 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:20.892 else:
2025-07-02 05:48:20.898 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:20.903 eqi = None
2025-07-02 05:48:20.908
2025-07-02 05:48:20.914 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:20.923 # identical
2025-07-02 05:48:20.929
2025-07-02 05:48:20.936 # pump out diffs from before the synch point
2025-07-02 05:48:20.944 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:20.951
2025-07-02 05:48:20.958 # do intraline marking on the synch pair
2025-07-02 05:48:20.965 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:20.976 if eqi is None:
2025-07-02 05:48:20.986 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:20.993 atags = btags = ""
2025-07-02 05:48:20.999 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:21.006 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:21.015 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:21.025 if tag == 'replace':
2025-07-02 05:48:21.033 atags += '^' * la
2025-07-02 05:48:21.040 btags += '^' * lb
2025-07-02 05:48:21.047 elif tag == 'delete':
2025-07-02 05:48:21.053 atags += '-' * la
2025-07-02 05:48:21.059 elif tag == 'insert':
2025-07-02 05:48:21.069 btags += '+' * lb
2025-07-02 05:48:21.079 elif tag == 'equal':
2025-07-02 05:48:21.086 atags += ' ' * la
2025-07-02 05:48:21.092 btags += ' ' * lb
2025-07-02 05:48:21.098 else:
2025-07-02 05:48:21.105 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:21.111 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:21.119 else:
2025-07-02 05:48:21.129 # the synch pair is identical
2025-07-02 05:48:21.137 yield '  ' + aelt
2025-07-02 05:48:21.144
2025-07-02 05:48:21.150 # pump out diffs from after the synch point
2025-07-02 05:48:21.157 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:21.163
2025-07-02 05:48:21.169 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:21.182 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:21.194
2025-07-02 05:48:21.209 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:21.220 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:21.229 alo = 186, ahi = 1101
2025-07-02 05:48:21.241 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:21.254 blo = 186, bhi = 1101
2025-07-02 05:48:21.265
2025-07-02 05:48:21.273 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:21.281 g = []
2025-07-02 05:48:21.290 if alo < ahi:
2025-07-02 05:48:21.296 if blo < bhi:
2025-07-02 05:48:21.303 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:21.311 else:
2025-07-02 05:48:21.322 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:21.331 elif blo < bhi:
2025-07-02 05:48:21.339 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:21.345
2025-07-02 05:48:21.351 >       yield from g
2025-07-02 05:48:21.359
2025-07-02 05:48:21.369 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:21.377 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:21.386
2025-07-02 05:48:21.396 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:21.405 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:21.412 alo = 186, ahi = 1101
2025-07-02 05:48:21.426 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:21.436 blo = 186, bhi = 1101
2025-07-02 05:48:21.443
2025-07-02 05:48:21.450 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:21.460 r"""
2025-07-02 05:48:21.471 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:21.480 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:21.488 synch point, and intraline difference marking is done on the
2025-07-02 05:48:21.494 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:21.506
2025-07-02 05:48:21.517 Example:
2025-07-02 05:48:21.524
2025-07-02 05:48:21.531 >>> d = Differ()
2025-07-02 05:48:21.542 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:21.552 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:21.561 >>> print(''.join(results), end="")
2025-07-02 05:48:21.568 - abcDefghiJkl
2025-07-02 05:48:21.583 + abcdefGhijkl
2025-07-02 05:48:21.601 """
2025-07-02 05:48:21.612
2025-07-02 05:48:21.624 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:21.637 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:21.648 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:21.657 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:21.665 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:21.672
2025-07-02 05:48:21.679 # search for the pair that matches best without being identical
2025-07-02 05:48:21.685 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:21.697 # on junk -- unless we have to)
2025-07-02 05:48:21.707 for j in range(blo, bhi):
2025-07-02 05:48:21.715 bj = b[j]
2025-07-02 05:48:21.723 cruncher.set_seq2(bj)
2025-07-02 05:48:21.730 for i in range(alo, ahi):
2025-07-02 05:48:21.741 ai = a[i]
2025-07-02 05:48:21.752 if ai == bj:
2025-07-02 05:48:21.760 if eqi is None:
2025-07-02 05:48:21.772 eqi, eqj = i, j
2025-07-02 05:48:21.782 continue
2025-07-02 05:48:21.794 cruncher.set_seq1(ai)
2025-07-02 05:48:21.801 # computing similarity is expensive, so use the quick
2025-07-02 05:48:21.814 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:21.828 # compares by a factor of 3.
2025-07-02 05:48:21.837 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:21.845 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:21.853 # of the computation is cached by cruncher
2025-07-02 05:48:21.863 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:21.877 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:21.886 cruncher.ratio() > best_ratio:
2025-07-02 05:48:21.895 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:21.902 if best_ratio < cutoff:
2025-07-02 05:48:21.909 # no non-identical "pretty close" pair
2025-07-02 05:48:21.915 if eqi is None:
2025-07-02 05:48:21.923 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:21.935 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:21.944 return
2025-07-02 05:48:21.954 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:21.961 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:21.967 else:
2025-07-02 05:48:21.974 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:21.981 eqi = None
2025-07-02 05:48:21.985
2025-07-02 05:48:21.991 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:21.997 # identical
2025-07-02 05:48:22.003
2025-07-02 05:48:22.010 # pump out diffs from before the synch point
2025-07-02 05:48:22.018 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:22.028
2025-07-02 05:48:22.041 # do intraline marking on the synch pair
2025-07-02 05:48:22.053 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:22.064 if eqi is None:
2025-07-02 05:48:22.073 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:22.079 atags = btags = ""
2025-07-02 05:48:22.084 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:22.089 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:22.095 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:22.100 if tag == 'replace':
2025-07-02 05:48:22.106 atags += '^' * la
2025-07-02 05:48:22.117 btags += '^' * lb
2025-07-02 05:48:22.128 elif tag == 'delete':
2025-07-02 05:48:22.136 atags += '-' * la
2025-07-02 05:48:22.143 elif tag == 'insert':
2025-07-02 05:48:22.149 btags += '+' * lb
2025-07-02 05:48:22.157 elif tag == 'equal':
2025-07-02 05:48:22.171 atags += ' ' * la
2025-07-02 05:48:22.183 btags += ' ' * lb
2025-07-02 05:48:22.191 else:
2025-07-02 05:48:22.199 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:22.208 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:22.219 else:
2025-07-02 05:48:22.231 # the synch pair is identical
2025-07-02 05:48:22.242 yield '  ' + aelt
2025-07-02 05:48:22.253
2025-07-02 05:48:22.264 # pump out diffs from after the synch point
2025-07-02 05:48:22.273 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:22.282
2025-07-02 05:48:22.292 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:22.300 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:22.307
2025-07-02 05:48:22.315 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:22.322 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:22.331 alo = 187, ahi = 1101
2025-07-02 05:48:22.343 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:22.351 blo = 187, bhi = 1101
2025-07-02 05:48:22.359
2025-07-02 05:48:22.367 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:22.375 g = []
2025-07-02 05:48:22.387 if alo < ahi:
2025-07-02 05:48:22.398 if blo < bhi:
2025-07-02 05:48:22.411 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:22.424 else:
2025-07-02 05:48:22.434 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:22.443 elif blo < bhi:
2025-07-02 05:48:22.454 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:22.463
2025-07-02 05:48:22.473 >       yield from g
2025-07-02 05:48:22.480
2025-07-02 05:48:22.486 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:22.494 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:22.501
2025-07-02 05:48:22.510 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:22.518 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:22.525 alo = 187, ahi = 1101
2025-07-02 05:48:22.531 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:22.537 blo = 187, bhi = 1101
2025-07-02 05:48:22.543
2025-07-02 05:48:22.550 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:22.561 r"""
2025-07-02 05:48:22.571 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:22.580 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:22.586 synch point, and intraline difference marking is done on the
2025-07-02 05:48:22.593 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:22.598
2025-07-02 05:48:22.603 Example:
2025-07-02 05:48:22.610
2025-07-02 05:48:22.616 >>> d = Differ()
2025-07-02 05:48:22.624 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:22.631 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:22.638 >>> print(''.join(results), end="")
2025-07-02 05:48:22.644 - abcDefghiJkl
2025-07-02 05:48:22.657 + abcdefGhijkl
2025-07-02 05:48:22.670 """
2025-07-02 05:48:22.676
2025-07-02 05:48:22.688 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:22.696 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:22.703 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:22.709 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:22.715 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:22.720
2025-07-02 05:48:22.726 # search for the pair that matches best without being identical
2025-07-02 05:48:22.733 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:22.739 # on junk -- unless we have to)
2025-07-02 05:48:22.745 for j in range(blo, bhi):
2025-07-02 05:48:22.751 bj = b[j]
2025-07-02 05:48:22.756 cruncher.set_seq2(bj)
2025-07-02 05:48:22.763 for i in range(alo, ahi):
2025-07-02 05:48:22.769 ai = a[i]
2025-07-02 05:48:22.775 if ai == bj:
2025-07-02 05:48:22.789 if eqi is None:
2025-07-02 05:48:22.796 eqi, eqj = i, j
2025-07-02 05:48:22.803 continue
2025-07-02 05:48:22.809 cruncher.set_seq1(ai)
2025-07-02 05:48:22.822 # computing similarity is expensive, so use the quick
2025-07-02 05:48:22.832 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:22.839 # compares by a factor of 3.
2025-07-02 05:48:22.846 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:22.851 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:22.856 # of the computation is cached by cruncher
2025-07-02 05:48:22.861 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:22.866 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:22.873 cruncher.ratio() > best_ratio:
2025-07-02 05:48:22.882 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:22.890 if best_ratio < cutoff:
2025-07-02 05:48:22.896 # no non-identical "pretty close" pair
2025-07-02 05:48:22.902 if eqi is None:
2025-07-02 05:48:22.915 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:22.927 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:22.935 return
2025-07-02 05:48:22.942 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:22.948 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:22.955 else:
2025-07-02 05:48:22.961 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:22.969 eqi = None
2025-07-02 05:48:22.975
2025-07-02 05:48:22.987 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:22.998 # identical
2025-07-02 05:48:23.007
2025-07-02 05:48:23.015 # pump out diffs from before the synch point
2025-07-02 05:48:23.022 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:23.028
2025-07-02 05:48:23.034 # do intraline marking on the synch pair
2025-07-02 05:48:23.039 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:23.045 if eqi is None:
2025-07-02 05:48:23.051 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:23.059 atags = btags = ""
2025-07-02 05:48:23.070 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:23.081 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:23.094 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:23.105 if tag == 'replace':
2025-07-02 05:48:23.115 atags += '^' * la
2025-07-02 05:48:23.123 btags += '^' * lb
2025-07-02 05:48:23.130 elif tag == 'delete':
2025-07-02 05:48:23.135 atags += '-' * la
2025-07-02 05:48:23.140 elif tag == 'insert':
2025-07-02 05:48:23.146 btags += '+' * lb
2025-07-02 05:48:23.152 elif tag == 'equal':
2025-07-02 05:48:23.158 atags += ' ' * la
2025-07-02 05:48:23.164 btags += ' ' * lb
2025-07-02 05:48:23.170 else:
2025-07-02 05:48:23.176 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:23.182 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:23.188 else:
2025-07-02 05:48:23.195 # the synch pair is identical
2025-07-02 05:48:23.201 yield '  ' + aelt
2025-07-02 05:48:23.209
2025-07-02 05:48:23.220 # pump out diffs from after the synch point
2025-07-02 05:48:23.231 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:23.240
2025-07-02 05:48:23.248 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:23.255 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:23.263
2025-07-02 05:48:23.272 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:23.279 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:23.285 alo = 188, ahi = 1101
2025-07-02 05:48:23.291 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:23.298 blo = 188, bhi = 1101
2025-07-02 05:48:23.305
2025-07-02 05:48:23.315 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:23.325 g = []
2025-07-02 05:48:23.337 if alo < ahi:
2025-07-02 05:48:23.348 if blo < bhi:
2025-07-02 05:48:23.358 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:23.366 else:
2025-07-02 05:48:23.374 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:23.387 elif blo < bhi:
2025-07-02 05:48:23.398 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:23.408
2025-07-02 05:48:23.415 >       yield from g
2025-07-02 05:48:23.422
2025-07-02 05:48:23.430 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:23.438 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:23.446
2025-07-02 05:48:23.453 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:23.460 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:23.469 alo = 188, ahi = 1101
2025-07-02 05:48:23.485 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:23.495 blo = 188, bhi = 1101
2025-07-02 05:48:23.503
2025-07-02 05:48:23.511 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:23.518 r"""
2025-07-02 05:48:23.525 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:23.533 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:23.539 synch point, and intraline difference marking is done on the
2025-07-02 05:48:23.546 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:23.554
2025-07-02 05:48:23.563 Example:
2025-07-02 05:48:23.570
2025-07-02 05:48:23.577 >>> d = Differ()
2025-07-02 05:48:23.588 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:23.598 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:23.606 >>> print(''.join(results), end="")
2025-07-02 05:48:23.617 - abcDefghiJkl
2025-07-02 05:48:23.636 + abcdefGhijkl
2025-07-02 05:48:23.649 """
2025-07-02 05:48:23.655
2025-07-02 05:48:23.663 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:23.674 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:23.683 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:23.691 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:23.700 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:23.708
2025-07-02 05:48:23.716 # search for the pair that matches best without being identical
2025-07-02 05:48:23.723 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:23.729 # on junk -- unless we have to)
2025-07-02 05:48:23.735 for j in range(blo, bhi):
2025-07-02 05:48:23.740 bj = b[j]
2025-07-02 05:48:23.746 cruncher.set_seq2(bj)
2025-07-02 05:48:23.756 for i in range(alo, ahi):
2025-07-02 05:48:23.766 ai = a[i]
2025-07-02 05:48:23.775 if ai == bj:
2025-07-02 05:48:23.783 if eqi is None:
2025-07-02 05:48:23.789 eqi, eqj = i, j
2025-07-02 05:48:23.797 continue
2025-07-02 05:48:23.803 cruncher.set_seq1(ai)
2025-07-02 05:48:23.809 # computing similarity is expensive, so use the quick
2025-07-02 05:48:23.815 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:23.821 # compares by a factor of 3.
2025-07-02 05:48:23.827 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:23.834 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:23.840 # of the computation is cached by cruncher
2025-07-02 05:48:23.849 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:23.860 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:23.872 cruncher.ratio() > best_ratio:
2025-07-02 05:48:23.882 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:23.896 if best_ratio < cutoff:
2025-07-02 05:48:23.908 # no non-identical "pretty close" pair
2025-07-02 05:48:23.921 if eqi is None:
2025-07-02 05:48:23.932 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:23.945 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:23.955 return
2025-07-02 05:48:23.968 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:23.977 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:23.984 else:
2025-07-02 05:48:23.992 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:23.998 eqi = None
2025-07-02 05:48:24.009
2025-07-02 05:48:24.018 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:24.026 # identical
2025-07-02 05:48:24.038
2025-07-02 05:48:24.049 # pump out diffs from before the synch point
2025-07-02 05:48:24.061 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:24.073
2025-07-02 05:48:24.083 # do intraline marking on the synch pair
2025-07-02 05:48:24.090 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:24.096 if eqi is None:
2025-07-02 05:48:24.107 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:24.116 atags = btags = ""
2025-07-02 05:48:24.123 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:24.130 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:24.138 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:24.153 if tag == 'replace':
2025-07-02 05:48:24.164 atags += '^' * la
2025-07-02 05:48:24.171 btags += '^' * lb
2025-07-02 05:48:24.178 elif tag == 'delete':
2025-07-02 05:48:24.185 atags += '-' * la
2025-07-02 05:48:24.193 elif tag == 'insert':
2025-07-02 05:48:24.200 btags += '+' * lb
2025-07-02 05:48:24.207 elif tag == 'equal':
2025-07-02 05:48:24.213 atags += ' ' * la
2025-07-02 05:48:24.220 btags += ' ' * lb
2025-07-02 05:48:24.227 else:
2025-07-02 05:48:24.233 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:24.242 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:24.249 else:
2025-07-02 05:48:24.258 # the synch pair is identical
2025-07-02 05:48:24.266 yield '  ' + aelt
2025-07-02 05:48:24.272
2025-07-02 05:48:24.277 # pump out diffs from after the synch point
2025-07-02 05:48:24.285 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:24.293
2025-07-02 05:48:24.303 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:24.311 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:24.317
2025-07-02 05:48:24.324 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:24.336 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:24.344 alo = 189, ahi = 1101
2025-07-02 05:48:24.352 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:24.359 blo = 189, bhi = 1101
2025-07-02 05:48:24.367
2025-07-02 05:48:24.379 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:24.389 g = []
2025-07-02 05:48:24.400 if alo < ahi:
2025-07-02 05:48:24.413 if blo < bhi:
2025-07-02 05:48:24.423 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:24.431 else:
2025-07-02 05:48:24.439 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:24.450 elif blo < bhi:
2025-07-02 05:48:24.460 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:24.468
2025-07-02 05:48:24.475 >       yield from g
2025-07-02 05:48:24.482
2025-07-02 05:48:24.496 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:24.510 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:24.521
2025-07-02 05:48:24.532 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:24.539 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:24.545 alo = 189, ahi = 1101
2025-07-02 05:48:24.551 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:24.556 blo = 189, bhi = 1101
2025-07-02 05:48:24.561
2025-07-02 05:48:24.566 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:24.571 r"""
2025-07-02 05:48:24.576 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:24.581 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:24.586 synch point, and intraline difference marking is done on the
2025-07-02 05:48:24.591 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:24.597
2025-07-02 05:48:24.603 Example:
2025-07-02 05:48:24.609
2025-07-02 05:48:24.616 >>> d = Differ()
2025-07-02 05:48:24.624 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:24.630 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:24.638 >>> print(''.join(results), end="")
2025-07-02 05:48:24.646 - abcDefghiJkl
2025-07-02 05:48:24.661 + abcdefGhijkl
2025-07-02 05:48:24.677 """
2025-07-02 05:48:24.683
2025-07-02 05:48:24.690 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:24.697 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:24.705 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:24.713 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:24.719 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:24.726
2025-07-02 05:48:24.733 # search for the pair that matches best without being identical
2025-07-02 05:48:24.739 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:24.745 # on junk -- unless we have to)
2025-07-02 05:48:24.754 for j in range(blo, bhi):
2025-07-02 05:48:24.761 bj = b[j]
2025-07-02 05:48:24.768 cruncher.set_seq2(bj)
2025-07-02 05:48:24.775 for i in range(alo, ahi):
2025-07-02 05:48:24.781 ai = a[i]
2025-07-02 05:48:24.787 if ai == bj:
2025-07-02 05:48:24.795 if eqi is None:
2025-07-02 05:48:24.803 eqi, eqj = i, j
2025-07-02 05:48:24.811 continue
2025-07-02 05:48:24.818 cruncher.set_seq1(ai)
2025-07-02 05:48:24.831 # computing similarity is expensive, so use the quick
2025-07-02 05:48:24.840 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:24.848 # compares by a factor of 3.
2025-07-02 05:48:24.854 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:24.861 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:24.869 # of the computation is cached by cruncher
2025-07-02 05:48:24.882 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:24.892 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:24.900 cruncher.ratio() > best_ratio:
2025-07-02 05:48:24.913 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:24.923 if best_ratio < cutoff:
2025-07-02 05:48:24.930 # no non-identical "pretty close" pair
2025-07-02 05:48:24.937 if eqi is None:
2025-07-02 05:48:24.944 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:24.958 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:24.969 return
2025-07-02 05:48:24.979 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:24.990 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:25.001 else:
2025-07-02 05:48:25.011 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:25.018 eqi = None
2025-07-02 05:48:25.024
2025-07-02 05:48:25.030 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:25.035 # identical
2025-07-02 05:48:25.040
2025-07-02 05:48:25.047 # pump out diffs from before the synch point
2025-07-02 05:48:25.054 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:25.061
2025-07-02 05:48:25.072 # do intraline marking on the synch pair
2025-07-02 05:48:25.083 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:25.096 if eqi is None:
2025-07-02 05:48:25.109 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:25.118 atags = btags = ""
2025-07-02 05:48:25.125 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:25.133 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:25.140 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:25.147 if tag == 'replace':
2025-07-02 05:48:25.153 atags += '^' * la
2025-07-02 05:48:25.165 btags += '^' * lb
2025-07-02 05:48:25.175 elif tag == 'delete':
2025-07-02 05:48:25.185 atags += '-' * la
2025-07-02 05:48:25.192 elif tag == 'insert':
2025-07-02 05:48:25.199 btags += '+' * lb
2025-07-02 05:48:25.204 elif tag == 'equal':
2025-07-02 05:48:25.209 atags += ' ' * la
2025-07-02 05:48:25.214 btags += ' ' * lb
2025-07-02 05:48:25.220 else:
2025-07-02 05:48:25.229 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:25.238 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:25.246 else:
2025-07-02 05:48:25.252 # the synch pair is identical
2025-07-02 05:48:25.261 yield '  ' + aelt
2025-07-02 05:48:25.268
2025-07-02 05:48:25.275 # pump out diffs from after the synch point
2025-07-02 05:48:25.281 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:25.288
2025-07-02 05:48:25.295 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:25.301 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:25.308
2025-07-02 05:48:25.318 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:25.329 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:25.336 alo = 190, ahi = 1101
2025-07-02 05:48:25.344 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:25.351 blo = 190, bhi = 1101
2025-07-02 05:48:25.359
2025-07-02 05:48:25.370 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:25.379 g = []
2025-07-02 05:48:25.387 if alo < ahi:
2025-07-02 05:48:25.394 if blo < bhi:
2025-07-02 05:48:25.401 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:25.411 else:
2025-07-02 05:48:25.422 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:25.430 elif blo < bhi:
2025-07-02 05:48:25.438 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:25.447
2025-07-02 05:48:25.455 >       yield from g
2025-07-02 05:48:25.462
2025-07-02 05:48:25.475 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:25.486 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:25.496
2025-07-02 05:48:25.505 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:25.517 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:25.526 alo = 190, ahi = 1101
2025-07-02 05:48:25.535 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:25.547 blo = 190, bhi = 1101
2025-07-02 05:48:25.556
2025-07-02 05:48:25.565 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:25.573 r"""
2025-07-02 05:48:25.581 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:25.587 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:25.592 synch point, and intraline difference marking is done on the
2025-07-02 05:48:25.599 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:25.605
2025-07-02 05:48:25.612 Example:
2025-07-02 05:48:25.619
2025-07-02 05:48:25.627 >>> d = Differ()
2025-07-02 05:48:25.634 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:25.642 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:25.651 >>> print(''.join(results), end="")
2025-07-02 05:48:25.660 - abcDefghiJkl
2025-07-02 05:48:25.683 + abcdefGhijkl
2025-07-02 05:48:25.708 """
2025-07-02 05:48:25.722
2025-07-02 05:48:25.732 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:25.743 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:25.757 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:25.768 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:25.777 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:25.785
2025-07-02 05:48:25.793 # search for the pair that matches best without being identical
2025-07-02 05:48:25.803 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:25.816 # on junk -- unless we have to)
2025-07-02 05:48:25.827 for j in range(blo, bhi):
2025-07-02 05:48:25.836 bj = b[j]
2025-07-02 05:48:25.845 cruncher.set_seq2(bj)
2025-07-02 05:48:25.852 for i in range(alo, ahi):
2025-07-02 05:48:25.857 ai = a[i]
2025-07-02 05:48:25.863 if ai == bj:
2025-07-02 05:48:25.869 if eqi is None:
2025-07-02 05:48:25.874 eqi, eqj = i, j
2025-07-02 05:48:25.879 continue
2025-07-02 05:48:25.886 cruncher.set_seq1(ai)
2025-07-02 05:48:25.901 # computing similarity is expensive, so use the quick
2025-07-02 05:48:25.910 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:25.917 # compares by a factor of 3.
2025-07-02 05:48:25.922 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:25.928 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:25.943 # of the computation is cached by cruncher
2025-07-02 05:48:25.954 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:25.960 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:25.967 cruncher.ratio() > best_ratio:
2025-07-02 05:48:25.974 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:25.981 if best_ratio < cutoff:
2025-07-02 05:48:25.992 # no non-identical "pretty close" pair
2025-07-02 05:48:26.003 if eqi is None:
2025-07-02 05:48:26.011 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:26.018 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:26.023 return
2025-07-02 05:48:26.029 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:26.035 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:26.040 else:
2025-07-02 05:48:26.045 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:26.050 eqi = None
2025-07-02 05:48:26.056
2025-07-02 05:48:26.063 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:26.073 # identical
2025-07-02 05:48:26.087
2025-07-02 05:48:26.099 # pump out diffs from before the synch point
2025-07-02 05:48:26.109 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:26.117
2025-07-02 05:48:26.124 # do intraline marking on the synch pair
2025-07-02 05:48:26.132 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:26.138 if eqi is None:
2025-07-02 05:48:26.147 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:26.160 atags = btags = ""
2025-07-02 05:48:26.171 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:26.178 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:26.188 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:26.197 if tag == 'replace':
2025-07-02 05:48:26.207 atags += '^' * la
2025-07-02 05:48:26.219 btags += '^' * lb
2025-07-02 05:48:26.228 elif tag == 'delete':
2025-07-02 05:48:26.243 atags += '-' * la
2025-07-02 05:48:26.253 elif tag == 'insert':
2025-07-02 05:48:26.261 btags += '+' * lb
2025-07-02 05:48:26.269 elif tag == 'equal':
2025-07-02 05:48:26.276 atags += ' ' * la
2025-07-02 05:48:26.285 btags += ' ' * lb
2025-07-02 05:48:26.293 else:
2025-07-02 05:48:26.308 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:26.318 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:26.325 else:
2025-07-02 05:48:26.334 # the synch pair is identical
2025-07-02 05:48:26.341 yield '  ' + aelt
2025-07-02 05:48:26.350
2025-07-02 05:48:26.357 # pump out diffs from after the synch point
2025-07-02 05:48:26.365 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:26.372
2025-07-02 05:48:26.379 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:26.386 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:26.395
2025-07-02 05:48:26.403 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:26.412 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:26.418 alo = 191, ahi = 1101
2025-07-02 05:48:26.427 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:26.432 blo = 191, bhi = 1101
2025-07-02 05:48:26.437
2025-07-02 05:48:26.443 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:26.451 g = []
2025-07-02 05:48:26.461 if alo < ahi:
2025-07-02 05:48:26.470 if blo < bhi:
2025-07-02 05:48:26.478 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:26.487 else:
2025-07-02 05:48:26.498 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:26.510 elif blo < bhi:
2025-07-02 05:48:26.522 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:26.534
2025-07-02 05:48:26.545 >       yield from g
2025-07-02 05:48:26.553
2025-07-02 05:48:26.566 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:26.580 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:26.591
2025-07-02 05:48:26.600 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:26.613 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:26.623 alo = 191, ahi = 1101
2025-07-02 05:48:26.634 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:26.641 blo = 191, bhi = 1101
2025-07-02 05:48:26.648
2025-07-02 05:48:26.654 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:26.660 r"""
2025-07-02 05:48:26.666 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:26.674 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:26.682 synch point, and intraline difference marking is done on the
2025-07-02 05:48:26.693 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:26.703
2025-07-02 05:48:26.710 Example:
2025-07-02 05:48:26.717
2025-07-02 05:48:26.725 >>> d = Differ()
2025-07-02 05:48:26.733 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:26.740 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:26.746 >>> print(''.join(results), end="")
2025-07-02 05:48:26.758 - abcDefghiJkl
2025-07-02 05:48:26.773 + abcdefGhijkl
2025-07-02 05:48:26.801 """
2025-07-02 05:48:26.809
2025-07-02 05:48:26.819 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:26.831 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:26.842 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:26.853 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:26.862 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:26.873
2025-07-02 05:48:26.881 # search for the pair that matches best without being identical
2025-07-02 05:48:26.887 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:26.901 # on junk -- unless we have to)
2025-07-02 05:48:26.911 for j in range(blo, bhi):
2025-07-02 05:48:26.918 bj = b[j]
2025-07-02 05:48:26.925 cruncher.set_seq2(bj)
2025-07-02 05:48:26.930 for i in range(alo, ahi):
2025-07-02 05:48:26.937 ai = a[i]
2025-07-02 05:48:26.942 if ai == bj:
2025-07-02 05:48:26.953 if eqi is None:
2025-07-02 05:48:26.965 eqi, eqj = i, j
2025-07-02 05:48:26.977 continue
2025-07-02 05:48:26.987 cruncher.set_seq1(ai)
2025-07-02 05:48:26.996 # computing similarity is expensive, so use the quick
2025-07-02 05:48:27.007 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:27.017 # compares by a factor of 3.
2025-07-02 05:48:27.025 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:27.032 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:27.040 # of the computation is cached by cruncher
2025-07-02 05:48:27.048 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:27.056 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:27.064 cruncher.ratio() > best_ratio:
2025-07-02 05:48:27.072 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:27.079 if best_ratio < cutoff:
2025-07-02 05:48:27.087 # no non-identical "pretty close" pair
2025-07-02 05:48:27.094 if eqi is None:
2025-07-02 05:48:27.102 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:27.115 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:27.124 return
2025-07-02 05:48:27.132 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:27.138 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:27.144 else:
2025-07-02 05:48:27.150 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:27.156 eqi = None
2025-07-02 05:48:27.162
2025-07-02 05:48:27.168 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:27.174 # identical
2025-07-02 05:48:27.180
2025-07-02 05:48:27.186 # pump out diffs from before the synch point
2025-07-02 05:48:27.199 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:27.213
2025-07-02 05:48:27.225 # do intraline marking on the synch pair
2025-07-02 05:48:27.233 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:27.239 if eqi is None:
2025-07-02 05:48:27.246 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:27.253 atags = btags = ""
2025-07-02 05:48:27.259 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:27.264 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:27.270 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:27.280 if tag == 'replace':
2025-07-02 05:48:27.289 atags += '^' * la
2025-07-02 05:48:27.297 btags += '^' * lb
2025-07-02 05:48:27.304 elif tag == 'delete':
2025-07-02 05:48:27.310 atags += '-' * la
2025-07-02 05:48:27.322 elif tag == 'insert':
2025-07-02 05:48:27.333 btags += '+' * lb
2025-07-02 05:48:27.342 elif tag == 'equal':
2025-07-02 05:48:27.352 atags += ' ' * la
2025-07-02 05:48:27.363 btags += ' ' * lb
2025-07-02 05:48:27.374 else:
2025-07-02 05:48:27.384 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:27.391 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:27.396 else:
2025-07-02 05:48:27.409 # the synch pair is identical
2025-07-02 05:48:27.419 yield '  ' + aelt
2025-07-02 05:48:27.430
2025-07-02 05:48:27.438 # pump out diffs from after the synch point
2025-07-02 05:48:27.447 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:27.460
2025-07-02 05:48:27.471 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:27.480 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:27.488
2025-07-02 05:48:27.494 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:27.501 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:27.508 alo = 192, ahi = 1101
2025-07-02 05:48:27.515 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:27.522 blo = 192, bhi = 1101
2025-07-02 05:48:27.530
2025-07-02 05:48:27.537 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:27.542 g = []
2025-07-02 05:48:27.552 if alo < ahi:
2025-07-02 05:48:27.562 if blo < bhi:
2025-07-02 05:48:27.569 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:27.575 else:
2025-07-02 05:48:27.580 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:27.587 elif blo < bhi:
2025-07-02 05:48:27.593 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:27.600
2025-07-02 05:48:27.607 >       yield from g
2025-07-02 05:48:27.613
2025-07-02 05:48:27.620 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:27.626 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:27.635
2025-07-02 05:48:27.646 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:27.654 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:27.663 alo = 192, ahi = 1101
2025-07-02 05:48:27.675 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:27.685 blo = 192, bhi = 1101
2025-07-02 05:48:27.693
2025-07-02 05:48:27.705 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:27.717 r"""
2025-07-02 05:48:27.728 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:27.735 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:27.747 synch point, and intraline difference marking is done on the
2025-07-02 05:48:27.758 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:27.768
2025-07-02 05:48:27.778 Example:
2025-07-02 05:48:27.792
2025-07-02 05:48:27.802 >>> d = Differ()
2025-07-02 05:48:27.811 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:27.821 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:27.830 >>> print(''.join(results), end="")
2025-07-02 05:48:27.842 - abcDefghiJkl
2025-07-02 05:48:27.859 + abcdefGhijkl
2025-07-02 05:48:27.874 """
2025-07-02 05:48:27.883
2025-07-02 05:48:27.894 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:27.904 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:27.911 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:27.917 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:27.923 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:27.929
2025-07-02 05:48:27.936 # search for the pair that matches best without being identical
2025-07-02 05:48:27.945 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:27.958 # on junk -- unless we have to)
2025-07-02 05:48:27.970 for j in range(blo, bhi):
2025-07-02 05:48:27.981 bj = b[j]
2025-07-02 05:48:27.995 cruncher.set_seq2(bj)
2025-07-02 05:48:28.005 for i in range(alo, ahi):
2025-07-02 05:48:28.018 ai = a[i]
2025-07-02 05:48:28.029 if ai == bj:
2025-07-02 05:48:28.037 if eqi is None:
2025-07-02 05:48:28.047 eqi, eqj = i, j
2025-07-02 05:48:28.059 continue
2025-07-02 05:48:28.068 cruncher.set_seq1(ai)
2025-07-02 05:48:28.076 # computing similarity is expensive, so use the quick
2025-07-02 05:48:28.083 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:28.089 # compares by a factor of 3.
2025-07-02 05:48:28.101 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:28.111 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:28.120 # of the computation is cached by cruncher
2025-07-02 05:48:28.130 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:28.138 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:28.145 cruncher.ratio() > best_ratio:
2025-07-02 05:48:28.157 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:28.166 if best_ratio < cutoff:
2025-07-02 05:48:28.173 # no non-identical "pretty close" pair
2025-07-02 05:48:28.184 if eqi is None:
2025-07-02 05:48:28.192 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:28.200 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:28.207 return
2025-07-02 05:48:28.213 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:28.218 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:28.229 else:
2025-07-02 05:48:28.241 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:28.251 eqi = None
2025-07-02 05:48:28.259
2025-07-02 05:48:28.266 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:28.273 # identical
2025-07-02 05:48:28.280
2025-07-02 05:48:28.286 # pump out diffs from before the synch point
2025-07-02 05:48:28.297 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:28.305
2025-07-02 05:48:28.312 # do intraline marking on the synch pair
2025-07-02 05:48:28.319 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:28.324 if eqi is None:
2025-07-02 05:48:28.329 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:28.334 atags = btags = ""
2025-07-02 05:48:28.343 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:28.352 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:28.364 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:28.377 if tag == 'replace':
2025-07-02 05:48:28.385 atags += '^' * la
2025-07-02 05:48:28.397 btags += '^' * lb
2025-07-02 05:48:28.406 elif tag == 'delete':
2025-07-02 05:48:28.419 atags += '-' * la
2025-07-02 05:48:28.428 elif tag == 'insert':
2025-07-02 05:48:28.435 btags += '+' * lb
2025-07-02 05:48:28.442 elif tag == 'equal':
2025-07-02 05:48:28.448 atags += ' ' * la
2025-07-02 05:48:28.455 btags += ' ' * lb
2025-07-02 05:48:28.462 else:
2025-07-02 05:48:28.472 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:28.489 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:28.503 else:
2025-07-02 05:48:28.516 # the synch pair is identical
2025-07-02 05:48:28.528 yield '  ' + aelt
2025-07-02 05:48:28.540
2025-07-02 05:48:28.550 # pump out diffs from after the synch point
2025-07-02 05:48:28.559 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:28.568
2025-07-02 05:48:28.581 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:28.596 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:28.607
2025-07-02 05:48:28.615 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:28.624 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:28.637 alo = 193, ahi = 1101
2025-07-02 05:48:28.650 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:28.658 blo = 193, bhi = 1101
2025-07-02 05:48:28.665
2025-07-02 05:48:28.671 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:28.678 g = []
2025-07-02 05:48:28.685 if alo < ahi:
2025-07-02 05:48:28.692 if blo < bhi:
2025-07-02 05:48:28.700 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:28.707 else:
2025-07-02 05:48:28.712 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:28.718 elif blo < bhi:
2025-07-02 05:48:28.729 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:28.738
2025-07-02 05:48:28.745 >       yield from g
2025-07-02 05:48:28.751
2025-07-02 05:48:28.768 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:28.780 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:28.793
2025-07-02 05:48:28.807 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:28.815 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:28.822 alo = 193, ahi = 1101
2025-07-02 05:48:28.830 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:28.835 blo = 193, bhi = 1101
2025-07-02 05:48:28.840
2025-07-02 05:48:28.846 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:28.851 r"""
2025-07-02 05:48:28.856 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:28.862 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:28.871 synch point, and intraline difference marking is done on the
2025-07-02 05:48:28.878 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:28.885
2025-07-02 05:48:28.891 Example:
2025-07-02 05:48:28.896
2025-07-02 05:48:28.902 >>> d = Differ()
2025-07-02 05:48:28.908 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:28.914 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:28.920 >>> print(''.join(results), end="")
2025-07-02 05:48:28.927 - abcDefghiJkl
2025-07-02 05:48:28.944 + abcdefGhijkl
2025-07-02 05:48:28.958 """
2025-07-02 05:48:28.967
2025-07-02 05:48:28.979 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:28.987 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:28.994 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:29.006 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:29.014 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:29.024
2025-07-02 05:48:29.036 # search for the pair that matches best without being identical
2025-07-02 05:48:29.044 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:29.051 # on junk -- unless we have to)
2025-07-02 05:48:29.059 for j in range(blo, bhi):
2025-07-02 05:48:29.067 bj = b[j]
2025-07-02 05:48:29.077 cruncher.set_seq2(bj)
2025-07-02 05:48:29.085 for i in range(alo, ahi):
2025-07-02 05:48:29.093 ai = a[i]
2025-07-02 05:48:29.101 if ai == bj:
2025-07-02 05:48:29.110 if eqi is None:
2025-07-02 05:48:29.118 eqi, eqj = i, j
2025-07-02 05:48:29.127 continue
2025-07-02 05:48:29.136 cruncher.set_seq1(ai)
2025-07-02 05:48:29.145 # computing similarity is expensive, so use the quick
2025-07-02 05:48:29.158 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:29.169 # compares by a factor of 3.
2025-07-02 05:48:29.178 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:29.184 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:29.193 # of the computation is cached by cruncher
2025-07-02 05:48:29.205 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:29.213 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:29.221 cruncher.ratio() > best_ratio:
2025-07-02 05:48:29.229 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:29.237 if best_ratio < cutoff:
2025-07-02 05:48:29.245 # no non-identical "pretty close" pair
2025-07-02 05:48:29.252 if eqi is None:
2025-07-02 05:48:29.261 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:29.269 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:29.276 return
2025-07-02 05:48:29.284 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:29.292 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:29.299 else:
2025-07-02 05:48:29.309 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:29.320 eqi = None
2025-07-02 05:48:29.328
2025-07-02 05:48:29.334 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:29.341 # identical
2025-07-02 05:48:29.347
2025-07-02 05:48:29.353 # pump out diffs from before the synch point
2025-07-02 05:48:29.360 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:29.366
2025-07-02 05:48:29.374 # do intraline marking on the synch pair
2025-07-02 05:48:29.383 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:29.395 if eqi is None:
2025-07-02 05:48:29.403 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:29.410 atags = btags = ""
2025-07-02 05:48:29.415 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:29.422 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:29.435 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:29.445 if tag == 'replace':
2025-07-02 05:48:29.452 atags += '^' * la
2025-07-02 05:48:29.459 btags += '^' * lb
2025-07-02 05:48:29.465 elif tag == 'delete':
2025-07-02 05:48:29.472 atags += '-' * la
2025-07-02 05:48:29.478 elif tag == 'insert':
2025-07-02 05:48:29.483 btags += '+' * lb
2025-07-02 05:48:29.490 elif tag == 'equal':
2025-07-02 05:48:29.497 atags += ' ' * la
2025-07-02 05:48:29.504 btags += ' ' * lb
2025-07-02 05:48:29.512 else:
2025-07-02 05:48:29.520 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:29.527 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:29.534 else:
2025-07-02 05:48:29.542 # the synch pair is identical
2025-07-02 05:48:29.549 yield '  ' + aelt
2025-07-02 05:48:29.556
2025-07-02 05:48:29.562 # pump out diffs from after the synch point
2025-07-02 05:48:29.569 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:29.576
2025-07-02 05:48:29.582 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:29.590 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:29.598
2025-07-02 05:48:29.605 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:29.611 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:29.617 alo = 194, ahi = 1101
2025-07-02 05:48:29.625 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:29.632 blo = 194, bhi = 1101
2025-07-02 05:48:29.639
2025-07-02 05:48:29.646 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:29.652 g = []
2025-07-02 05:48:29.659 if alo < ahi:
2025-07-02 05:48:29.667 if blo < bhi:
2025-07-02 05:48:29.678 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:29.687 else:
2025-07-02 05:48:29.696 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:29.708 elif blo < bhi:
2025-07-02 05:48:29.717 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:29.725
2025-07-02 05:48:29.734 >       yield from g
2025-07-02 05:48:29.743
2025-07-02 05:48:29.754 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:29.763 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:29.770
2025-07-02 05:48:29.777 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:29.784 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:29.791 alo = 194, ahi = 1101
2025-07-02 05:48:29.799 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:29.808 blo = 194, bhi = 1101
2025-07-02 05:48:29.819
2025-07-02 05:48:29.830 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:29.841 r"""
2025-07-02 05:48:29.850 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:29.857 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:29.865 synch point, and intraline difference marking is done on the
2025-07-02 05:48:29.873 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:29.880
2025-07-02 05:48:29.888 Example:
2025-07-02 05:48:29.896
2025-07-02 05:48:29.909 >>> d = Differ()
2025-07-02 05:48:29.920 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:29.928 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:29.935 >>> print(''.join(results), end="")
2025-07-02 05:48:29.941 - abcDefghiJkl
2025-07-02 05:48:29.953 + abcdefGhijkl
2025-07-02 05:48:29.969 """
2025-07-02 05:48:29.975
2025-07-02 05:48:29.980 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:29.986 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:29.991 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:29.996 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:30.002 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:30.012
2025-07-02 05:48:30.020 # search for the pair that matches best without being identical
2025-07-02 05:48:30.028 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:30.036 # on junk -- unless we have to)
2025-07-02 05:48:30.044 for j in range(blo, bhi):
2025-07-02 05:48:30.052 bj = b[j]
2025-07-02 05:48:30.059 cruncher.set_seq2(bj)
2025-07-02 05:48:30.066 for i in range(alo, ahi):
2025-07-02 05:48:30.075 ai = a[i]
2025-07-02 05:48:30.087 if ai == bj:
2025-07-02 05:48:30.097 if eqi is None:
2025-07-02 05:48:30.103 eqi, eqj = i, j
2025-07-02 05:48:30.110 continue
2025-07-02 05:48:30.116 cruncher.set_seq1(ai)
2025-07-02 05:48:30.123 # computing similarity is expensive, so use the quick
2025-07-02 05:48:30.129 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:30.136 # compares by a factor of 3.
2025-07-02 05:48:30.144 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:30.152 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:30.159 # of the computation is cached by cruncher
2025-07-02 05:48:30.167 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:30.175 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:30.182 cruncher.ratio() > best_ratio:
2025-07-02 05:48:30.190 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:30.197 if best_ratio < cutoff:
2025-07-02 05:48:30.210 # no non-identical "pretty close" pair
2025-07-02 05:48:30.221 if eqi is None:
2025-07-02 05:48:30.232 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:30.241 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:30.251 return
2025-07-02 05:48:30.259 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:30.265 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:30.275 else:
2025-07-02 05:48:30.282 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:30.289 eqi = None
2025-07-02 05:48:30.296
2025-07-02 05:48:30.302 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:30.307 # identical
2025-07-02 05:48:30.311
2025-07-02 05:48:30.316 # pump out diffs from before the synch point
2025-07-02 05:48:30.321 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:30.334
2025-07-02 05:48:30.348 # do intraline marking on the synch pair
2025-07-02 05:48:30.361 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:30.373 if eqi is None:
2025-07-02 05:48:30.384 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:30.397 atags = btags = ""
2025-07-02 05:48:30.408 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:30.417 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:30.427 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:30.435 if tag == 'replace':
2025-07-02 05:48:30.444 atags += '^' * la
2025-07-02 05:48:30.452 btags += '^' * lb
2025-07-02 05:48:30.458 elif tag == 'delete':
2025-07-02 05:48:30.464 atags += '-' * la
2025-07-02 05:48:30.469 elif tag == 'insert':
2025-07-02 05:48:30.475 btags += '+' * lb
2025-07-02 05:48:30.482 elif tag == 'equal':
2025-07-02 05:48:30.488 atags += ' ' * la
2025-07-02 05:48:30.494 btags += ' ' * lb
2025-07-02 05:48:30.500 else:
2025-07-02 05:48:30.506 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:30.515 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:30.525 else:
2025-07-02 05:48:30.533 # the synch pair is identical
2025-07-02 05:48:30.540 yield '  ' + aelt
2025-07-02 05:48:30.546
2025-07-02 05:48:30.553 # pump out diffs from after the synch point
2025-07-02 05:48:30.566 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:30.579
2025-07-02 05:48:30.593 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:30.602 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:30.614
2025-07-02 05:48:30.625 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:30.633 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:30.644 alo = 195, ahi = 1101
2025-07-02 05:48:30.657 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:30.667 blo = 195, bhi = 1101
2025-07-02 05:48:30.677
2025-07-02 05:48:30.688 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:30.696 g = []
2025-07-02 05:48:30.706 if alo < ahi:
2025-07-02 05:48:30.717 if blo < bhi:
2025-07-02 05:48:30.725 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:30.731 else:
2025-07-02 05:48:30.738 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:30.746 elif blo < bhi:
2025-07-02 05:48:30.752 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:30.758
2025-07-02 05:48:30.765 >       yield from g
2025-07-02 05:48:30.772
2025-07-02 05:48:30.785 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:30.797 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:30.809
2025-07-02 05:48:30.823 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:30.833 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:30.843 alo = 195, ahi = 1101
2025-07-02 05:48:30.856 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:30.867 blo = 195, bhi = 1101
2025-07-02 05:48:30.876
2025-07-02 05:48:30.883 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:30.889 r"""
2025-07-02 05:48:30.896 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:30.902 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:30.908 synch point, and intraline difference marking is done on the
2025-07-02 05:48:30.913 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:30.925
2025-07-02 05:48:30.933 Example:
2025-07-02 05:48:30.940
2025-07-02 05:48:30.948 >>> d = Differ()
2025-07-02 05:48:30.957 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:30.970 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:30.981 >>> print(''.join(results), end="")
2025-07-02 05:48:30.993 - abcDefghiJkl
2025-07-02 05:48:31.022 + abcdefGhijkl
2025-07-02 05:48:31.051 """
2025-07-02 05:48:31.061
2025-07-02 05:48:31.072 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:31.083 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:31.091 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:31.098 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:31.107 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:31.119
2025-07-02 05:48:31.129 # search for the pair that matches best without being identical
2025-07-02 05:48:31.136 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:31.143 # on junk -- unless we have to)
2025-07-02 05:48:31.154 for j in range(blo, bhi):
2025-07-02 05:48:31.167 bj = b[j]
2025-07-02 05:48:31.181 cruncher.set_seq2(bj)
2025-07-02 05:48:31.190 for i in range(alo, ahi):
2025-07-02 05:48:31.202 ai = a[i]
2025-07-02 05:48:31.213 if ai == bj:
2025-07-02 05:48:31.225 if eqi is None:
2025-07-02 05:48:31.236 eqi, eqj = i, j
2025-07-02 05:48:31.248 continue
2025-07-02 05:48:31.259 cruncher.set_seq1(ai)
2025-07-02 05:48:31.271 # computing similarity is expensive, so use the quick
2025-07-02 05:48:31.283 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:31.297 # compares by a factor of 3.
2025-07-02 05:48:31.309 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:31.322 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:31.332 # of the computation is cached by cruncher
2025-07-02 05:48:31.344 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:31.353 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:31.360 cruncher.ratio() > best_ratio:
2025-07-02 05:48:31.367 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:31.375 if best_ratio < cutoff:
2025-07-02 05:48:31.382 # no non-identical "pretty close" pair
2025-07-02 05:48:31.393 if eqi is None:
2025-07-02 05:48:31.406 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:31.414 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:31.421 return
2025-07-02 05:48:31.427 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:31.434 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:31.443 else:
2025-07-02 05:48:31.450 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:31.457 eqi = None
2025-07-02 05:48:31.463
2025-07-02 05:48:31.469 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:31.475 # identical
2025-07-02 05:48:31.481
2025-07-02 05:48:31.493 # pump out diffs from before the synch point
2025-07-02 05:48:31.503 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:31.514
2025-07-02 05:48:31.521 # do intraline marking on the synch pair
2025-07-02 05:48:31.526 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:31.532 if eqi is None:
2025-07-02 05:48:31.538 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:31.543 atags = btags = ""
2025-07-02 05:48:31.549 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:31.555 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:31.563 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:31.575 if tag == 'replace':
2025-07-02 05:48:31.583 atags += '^' * la
2025-07-02 05:48:31.589 btags += '^' * lb
2025-07-02 05:48:31.596 elif tag == 'delete':
2025-07-02 05:48:31.603 atags += '-' * la
2025-07-02 05:48:31.614 elif tag == 'insert':
2025-07-02 05:48:31.635 btags += '+' * lb
2025-07-02 05:48:31.647 elif tag == 'equal':
2025-07-02 05:48:31.654 atags += ' ' * la
2025-07-02 05:48:31.662 btags += ' ' * lb
2025-07-02 05:48:31.667 else:
2025-07-02 05:48:31.672 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:31.678 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:31.684 else:
2025-07-02 05:48:31.691 # the synch pair is identical
2025-07-02 05:48:31.696 yield '  ' + aelt
2025-07-02 05:48:31.702
2025-07-02 05:48:31.710 # pump out diffs from after the synch point
2025-07-02 05:48:31.718 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:31.724
2025-07-02 05:48:31.729 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:31.742 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:31.754
2025-07-02 05:48:31.764 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:31.778 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:31.789 alo = 196, ahi = 1101
2025-07-02 05:48:31.797 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:31.803 blo = 196, bhi = 1101
2025-07-02 05:48:31.808
2025-07-02 05:48:31.821 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:31.832 g = []
2025-07-02 05:48:31.841 if alo < ahi:
2025-07-02 05:48:31.848 if blo < bhi:
2025-07-02 05:48:31.859 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:31.871 else:
2025-07-02 05:48:31.880 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:31.888 elif blo < bhi:
2025-07-02 05:48:31.894 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:31.903
2025-07-02 05:48:31.913 >       yield from g
2025-07-02 05:48:31.922
2025-07-02 05:48:31.930 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:31.936 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:31.942
2025-07-02 05:48:31.948 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:31.959 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:31.967 alo = 196, ahi = 1101
2025-07-02 05:48:31.975 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:31.987 blo = 196, bhi = 1101
2025-07-02 05:48:31.998
2025-07-02 05:48:32.005 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:32.015 r"""
2025-07-02 05:48:32.024 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:32.033 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:32.041 synch point, and intraline difference marking is done on the
2025-07-02 05:48:32.049 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:32.059
2025-07-02 05:48:32.069 Example:
2025-07-02 05:48:32.077
2025-07-02 05:48:32.084 >>> d = Differ()
2025-07-02 05:48:32.092 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:32.100 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:32.107 >>> print(''.join(results), end="")
2025-07-02 05:48:32.115 - abcDefghiJkl
2025-07-02 05:48:32.134 + abcdefGhijkl
2025-07-02 05:48:32.150 """
2025-07-02 05:48:32.155
2025-07-02 05:48:32.163 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:32.174 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:32.182 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:32.189 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:32.197 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:32.204
2025-07-02 05:48:32.212 # search for the pair that matches best without being identical
2025-07-02 05:48:32.219 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:32.227 # on junk -- unless we have to)
2025-07-02 05:48:32.234 for j in range(blo, bhi):
2025-07-02 05:48:32.243 bj = b[j]
2025-07-02 05:48:32.256 cruncher.set_seq2(bj)
2025-07-02 05:48:32.266 for i in range(alo, ahi):
2025-07-02 05:48:32.276 ai = a[i]
2025-07-02 05:48:32.285 if ai == bj:
2025-07-02 05:48:32.293 if eqi is None:
2025-07-02 05:48:32.301 eqi, eqj = i, j
2025-07-02 05:48:32.313 continue
2025-07-02 05:48:32.324 cruncher.set_seq1(ai)
2025-07-02 05:48:32.337 # computing similarity is expensive, so use the quick
2025-07-02 05:48:32.349 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:32.358 # compares by a factor of 3.
2025-07-02 05:48:32.366 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:32.376 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:32.382 # of the computation is cached by cruncher
2025-07-02 05:48:32.390 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:32.398 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:32.405 cruncher.ratio() > best_ratio:
2025-07-02 05:48:32.412 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:32.419 if best_ratio < cutoff:
2025-07-02 05:48:32.426 # no non-identical "pretty close" pair
2025-07-02 05:48:32.433 if eqi is None:
2025-07-02 05:48:32.440 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:32.453 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:32.463 return
2025-07-02 05:48:32.471 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:32.479 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:32.490 else:
2025-07-02 05:48:32.502 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:32.512 eqi = None
2025-07-02 05:48:32.521
2025-07-02 05:48:32.527 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:32.533 # identical
2025-07-02 05:48:32.545
2025-07-02 05:48:32.557 # pump out diffs from before the synch point
2025-07-02 05:48:32.568 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:32.576
2025-07-02 05:48:32.583 # do intraline marking on the synch pair
2025-07-02 05:48:32.590 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:32.601 if eqi is None:
2025-07-02 05:48:32.611 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:32.618 atags = btags = ""
2025-07-02 05:48:32.627 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:32.640 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:32.651 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:32.662 if tag == 'replace':
2025-07-02 05:48:32.672 atags += '^' * la
2025-07-02 05:48:32.680 btags += '^' * lb
2025-07-02 05:48:32.686 elif tag == 'delete':
2025-07-02 05:48:32.691 atags += '-' * la
2025-07-02 05:48:32.696 elif tag == 'insert':
2025-07-02 05:48:32.700 btags += '+' * lb
2025-07-02 05:48:32.706 elif tag == 'equal':
2025-07-02 05:48:32.711 atags += ' ' * la
2025-07-02 05:48:32.717 btags += ' ' * lb
2025-07-02 05:48:32.724 else:
2025-07-02 05:48:32.731 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:32.739 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:32.746 else:
2025-07-02 05:48:32.753 # the synch pair is identical
2025-07-02 05:48:32.759 yield '  ' + aelt
2025-07-02 05:48:32.765
2025-07-02 05:48:32.771 # pump out diffs from after the synch point
2025-07-02 05:48:32.778 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:32.785
2025-07-02 05:48:32.792 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:32.799 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:32.806
2025-07-02 05:48:32.815 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:32.828 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:32.836 alo = 197, ahi = 1101
2025-07-02 05:48:32.842 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:32.848 blo = 197, bhi = 1101
2025-07-02 05:48:32.853
2025-07-02 05:48:32.858 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:32.862 g = []
2025-07-02 05:48:32.870 if alo < ahi:
2025-07-02 05:48:32.880 if blo < bhi:
2025-07-02 05:48:32.889 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:32.896 else:
2025-07-02 05:48:32.903 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:32.910 elif blo < bhi:
2025-07-02 05:48:32.917 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:32.925
2025-07-02 05:48:32.932 >       yield from g
2025-07-02 05:48:32.939
2025-07-02 05:48:32.946 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:32.953 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:32.961
2025-07-02 05:48:32.968 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:32.982 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:32.993 alo = 197, ahi = 1101
2025-07-02 05:48:33.002 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:33.009 blo = 197, bhi = 1101
2025-07-02 05:48:33.014
2025-07-02 05:48:33.018 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:33.023 r"""
2025-07-02 05:48:33.027 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:33.032 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:33.038 synch point, and intraline difference marking is done on the
2025-07-02 05:48:33.044 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:33.049
2025-07-02 05:48:33.055 Example:
2025-07-02 05:48:33.061
2025-07-02 05:48:33.069 >>> d = Differ()
2025-07-02 05:48:33.076 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:33.083 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:33.090 >>> print(''.join(results), end="")
2025-07-02 05:48:33.099 - abcDefghiJkl
2025-07-02 05:48:33.120 + abcdefGhijkl
2025-07-02 05:48:33.134 """
2025-07-02 05:48:33.140
2025-07-02 05:48:33.146 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:33.152 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:33.159 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:33.169 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:33.180 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:33.192
2025-07-02 05:48:33.204 # search for the pair that matches best without being identical
2025-07-02 05:48:33.214 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:33.224 # on junk -- unless we have to)
2025-07-02 05:48:33.233 for j in range(blo, bhi):
2025-07-02 05:48:33.239 bj = b[j]
2025-07-02 05:48:33.250 cruncher.set_seq2(bj)
2025-07-02 05:48:33.258 for i in range(alo, ahi):
2025-07-02 05:48:33.267 ai = a[i]
2025-07-02 05:48:33.277 if ai == bj:
2025-07-02 05:48:33.285 if eqi is None:
2025-07-02 05:48:33.296 eqi, eqj = i, j
2025-07-02 05:48:33.305 continue
2025-07-02 05:48:33.313 cruncher.set_seq1(ai)
2025-07-02 05:48:33.319 # computing similarity is expensive, so use the quick
2025-07-02 05:48:33.325 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:33.330 # compares by a factor of 3.
2025-07-02 05:48:33.335 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:33.339 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:33.344 # of the computation is cached by cruncher
2025-07-02 05:48:33.350 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:33.355 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:33.362 cruncher.ratio() > best_ratio:
2025-07-02 05:48:33.367 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:33.372 if best_ratio < cutoff:
2025-07-02 05:48:33.379 # no non-identical "pretty close" pair
2025-07-02 05:48:33.385 if eqi is None:
2025-07-02 05:48:33.391 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:33.396 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:33.401 return
2025-07-02 05:48:33.411 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:33.425 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:33.434 else:
2025-07-02 05:48:33.446 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:33.458 eqi = None
2025-07-02 05:48:33.471
2025-07-02 05:48:33.483 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:33.495 # identical
2025-07-02 05:48:33.504
2025-07-02 05:48:33.511 # pump out diffs from before the synch point
2025-07-02 05:48:33.519 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:33.529
2025-07-02 05:48:33.540 # do intraline marking on the synch pair
2025-07-02 05:48:33.549 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:33.561 if eqi is None:
2025-07-02 05:48:33.572 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:33.581 atags = btags = ""
2025-07-02 05:48:33.588 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:33.595 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:33.601 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:33.607 if tag == 'replace':
2025-07-02 05:48:33.614 atags += '^' * la
2025-07-02 05:48:33.620 btags += '^' * lb
2025-07-02 05:48:33.626 elif tag == 'delete':
2025-07-02 05:48:33.632 atags += '-' * la
2025-07-02 05:48:33.638 elif tag == 'insert':
2025-07-02 05:48:33.644 btags += '+' * lb
2025-07-02 05:48:33.651 elif tag == 'equal':
2025-07-02 05:48:33.657 atags += ' ' * la
2025-07-02 05:48:33.663 btags += ' ' * lb
2025-07-02 05:48:33.667 else:
2025-07-02 05:48:33.672 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:33.686 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:33.697 else:
2025-07-02 05:48:33.710 # the synch pair is identical
2025-07-02 05:48:33.718 yield '  ' + aelt
2025-07-02 05:48:33.724
2025-07-02 05:48:33.732 # pump out diffs from after the synch point
2025-07-02 05:48:33.739 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:33.747
2025-07-02 05:48:33.755 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:33.763 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:33.769
2025-07-02 05:48:33.776 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:33.783 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:33.788 alo = 198, ahi = 1101
2025-07-02 05:48:33.794 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:33.799 blo = 198, bhi = 1101
2025-07-02 05:48:33.804
2025-07-02 05:48:33.809 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:33.814 g = []
2025-07-02 05:48:33.819 if alo < ahi:
2025-07-02 05:48:33.824 if blo < bhi:
2025-07-02 05:48:33.829 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:33.834 else:
2025-07-02 05:48:33.839 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:33.846 elif blo < bhi:
2025-07-02 05:48:33.852 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:33.858
2025-07-02 05:48:33.865 >       yield from g
2025-07-02 05:48:33.871
2025-07-02 05:48:33.880 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:33.887 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:33.894
2025-07-02 05:48:33.900 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:33.908 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:33.915 alo = 198, ahi = 1101
2025-07-02 05:48:33.925 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:33.933 blo = 198, bhi = 1101
2025-07-02 05:48:33.940
2025-07-02 05:48:33.948 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:33.956 r"""
2025-07-02 05:48:33.965 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:33.972 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:33.980 synch point, and intraline difference marking is done on the
2025-07-02 05:48:33.988 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:33.995
2025-07-02 05:48:34.004 Example:
2025-07-02 05:48:34.016
2025-07-02 05:48:34.025 >>> d = Differ()
2025-07-02 05:48:34.033 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:34.042 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:34.055 >>> print(''.join(results), end="")
2025-07-02 05:48:34.063 - abcDefghiJkl
2025-07-02 05:48:34.076 + abcdefGhijkl
2025-07-02 05:48:34.094 """
2025-07-02 05:48:34.102
2025-07-02 05:48:34.112 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:34.125 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:34.132 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:34.138 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:34.143 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:34.151
2025-07-02 05:48:34.158 # search for the pair that matches best without being identical
2025-07-02 05:48:34.165 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:34.172 # on junk -- unless we have to)
2025-07-02 05:48:34.179 for j in range(blo, bhi):
2025-07-02 05:48:34.186 bj = b[j]
2025-07-02 05:48:34.195 cruncher.set_seq2(bj)
2025-07-02 05:48:34.207 for i in range(alo, ahi):
2025-07-02 05:48:34.216 ai = a[i]
2025-07-02 05:48:34.223 if ai == bj:
2025-07-02 05:48:34.230 if eqi is None:
2025-07-02 05:48:34.236 eqi, eqj = i, j
2025-07-02 05:48:34.242 continue
2025-07-02 05:48:34.248 cruncher.set_seq1(ai)
2025-07-02 05:48:34.255 # computing similarity is expensive, so use the quick
2025-07-02 05:48:34.261 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:34.266 # compares by a factor of 3.
2025-07-02 05:48:34.273 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:34.280 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:34.286 # of the computation is cached by cruncher
2025-07-02 05:48:34.298 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:34.307 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:34.315 cruncher.ratio() > best_ratio:
2025-07-02 05:48:34.325 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:34.337 if best_ratio < cutoff:
2025-07-02 05:48:34.348 # no non-identical "pretty close" pair
2025-07-02 05:48:34.361 if eqi is None:
2025-07-02 05:48:34.374 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:34.385 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:34.396 return
2025-07-02 05:48:34.409 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:34.417 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:34.423 else:
2025-07-02 05:48:34.429 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:34.434 eqi = None
2025-07-02 05:48:34.439
2025-07-02 05:48:34.447 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:34.459 # identical
2025-07-02 05:48:34.471
2025-07-02 05:48:34.482 # pump out diffs from before the synch point
2025-07-02 05:48:34.493 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:34.503
2025-07-02 05:48:34.512 # do intraline marking on the synch pair
2025-07-02 05:48:34.520 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:34.527 if eqi is None:
2025-07-02 05:48:34.533 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:34.539 atags = btags = ""
2025-07-02 05:48:34.544 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:34.551 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:34.556 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:34.561 if tag == 'replace':
2025-07-02 05:48:34.567 atags += '^' * la
2025-07-02 05:48:34.572 btags += '^' * lb
2025-07-02 05:48:34.584 elif tag == 'delete':
2025-07-02 05:48:34.596 atags += '-' * la
2025-07-02 05:48:34.605 elif tag == 'insert':
2025-07-02 05:48:34.612 btags += '+' * lb
2025-07-02 05:48:34.626 elif tag == 'equal':
2025-07-02 05:48:34.637 atags += ' ' * la
2025-07-02 05:48:34.646 btags += ' ' * lb
2025-07-02 05:48:34.661 else:
2025-07-02 05:48:34.673 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:34.684 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:34.695 else:
2025-07-02 05:48:34.705 # the synch pair is identical
2025-07-02 05:48:34.716 yield '  ' + aelt
2025-07-02 05:48:34.727
2025-07-02 05:48:34.736 # pump out diffs from after the synch point
2025-07-02 05:48:34.744 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:34.751
2025-07-02 05:48:34.757 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:34.768 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:34.778
2025-07-02 05:48:34.788 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:34.798 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:34.805 alo = 199, ahi = 1101
2025-07-02 05:48:34.815 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:34.828 blo = 199, bhi = 1101
2025-07-02 05:48:34.836
2025-07-02 05:48:34.846 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:34.859 g = []
2025-07-02 05:48:34.871 if alo < ahi:
2025-07-02 05:48:34.885 if blo < bhi:
2025-07-02 05:48:34.895 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:34.904 else:
2025-07-02 05:48:34.912 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:34.919 elif blo < bhi:
2025-07-02 05:48:34.927 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:34.933
2025-07-02 05:48:34.939 >       yield from g
2025-07-02 05:48:34.945
2025-07-02 05:48:34.951 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:34.957 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:34.963
2025-07-02 05:48:34.969 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:34.979 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:34.989 alo = 199, ahi = 1101
2025-07-02 05:48:34.997 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:35.005 blo = 199, bhi = 1101
2025-07-02 05:48:35.011
2025-07-02 05:48:35.018 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:35.027 r"""
2025-07-02 05:48:35.037 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:35.046 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:35.053 synch point, and intraline difference marking is done on the
2025-07-02 05:48:35.065 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:35.077
2025-07-02 05:48:35.086 Example:
2025-07-02 05:48:35.095
2025-07-02 05:48:35.106 >>> d = Differ()
2025-07-02 05:48:35.118 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:35.131 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:35.143 >>> print(''.join(results), end="")
2025-07-02 05:48:35.152 - abcDefghiJkl
2025-07-02 05:48:35.169 + abcdefGhijkl
2025-07-02 05:48:35.188 """
2025-07-02 05:48:35.199
2025-07-02 05:48:35.209 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:35.221 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:35.231 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:35.239 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:35.248 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:35.254
2025-07-02 05:48:35.264 # search for the pair that matches best without being identical
2025-07-02 05:48:35.272 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:35.280 # on junk -- unless we have to)
2025-07-02 05:48:35.286 for j in range(blo, bhi):
2025-07-02 05:48:35.292 bj = b[j]
2025-07-02 05:48:35.305 cruncher.set_seq2(bj)
2025-07-02 05:48:35.317 for i in range(alo, ahi):
2025-07-02 05:48:35.326 ai = a[i]
2025-07-02 05:48:35.336 if ai == bj:
2025-07-02 05:48:35.350 if eqi is None:
2025-07-02 05:48:35.361 eqi, eqj = i, j
2025-07-02 05:48:35.373 continue
2025-07-02 05:48:35.385 cruncher.set_seq1(ai)
2025-07-02 05:48:35.394 # computing similarity is expensive, so use the quick
2025-07-02 05:48:35.400 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:35.406 # compares by a factor of 3.
2025-07-02 05:48:35.412 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:35.425 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:35.433 # of the computation is cached by cruncher
2025-07-02 05:48:35.444 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:35.457 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:35.471 cruncher.ratio() > best_ratio:
2025-07-02 05:48:35.481 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:35.489 if best_ratio < cutoff:
2025-07-02 05:48:35.496 # no non-identical "pretty close" pair
2025-07-02 05:48:35.503 if eqi is None:
2025-07-02 05:48:35.516 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:35.525 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:35.537 return
2025-07-02 05:48:35.551 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:35.563 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:35.577 else:
2025-07-02 05:48:35.590 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:35.603 eqi = None
2025-07-02 05:48:35.612
2025-07-02 05:48:35.620 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:35.627 # identical
2025-07-02 05:48:35.635
2025-07-02 05:48:35.646 # pump out diffs from before the synch point
2025-07-02 05:48:35.655 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:35.662
2025-07-02 05:48:35.673 # do intraline marking on the synch pair
2025-07-02 05:48:35.682 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:35.692 if eqi is None:
2025-07-02 05:48:35.701 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:35.708 atags = btags = ""
2025-07-02 05:48:35.715 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:35.721 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:35.727 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:35.734 if tag == 'replace':
2025-07-02 05:48:35.744 atags += '^' * la
2025-07-02 05:48:35.758 btags += '^' * lb
2025-07-02 05:48:35.770 elif tag == 'delete':
2025-07-02 05:48:35.779 atags += '-' * la
2025-07-02 05:48:35.787 elif tag == 'insert':
2025-07-02 05:48:35.795 btags += '+' * lb
2025-07-02 05:48:35.801 elif tag == 'equal':
2025-07-02 05:48:35.808 atags += ' ' * la
2025-07-02 05:48:35.814 btags += ' ' * lb
2025-07-02 05:48:35.825 else:
2025-07-02 05:48:35.832 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:35.838 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:35.844 else:
2025-07-02 05:48:35.850 # the synch pair is identical
2025-07-02 05:48:35.857 yield '  ' + aelt
2025-07-02 05:48:35.863
2025-07-02 05:48:35.870 # pump out diffs from after the synch point
2025-07-02 05:48:35.877 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:35.884
2025-07-02 05:48:35.891 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:35.900 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:35.907
2025-07-02 05:48:35.916 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:35.924 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:35.931 alo = 202, ahi = 1101
2025-07-02 05:48:35.940 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:35.947 blo = 202, bhi = 1101
2025-07-02 05:48:35.955
2025-07-02 05:48:35.962 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:35.970 g = []
2025-07-02 05:48:35.981 if alo < ahi:
2025-07-02 05:48:35.991 if blo < bhi:
2025-07-02 05:48:36.000 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:36.009 else:
2025-07-02 05:48:36.019 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:36.026 elif blo < bhi:
2025-07-02 05:48:36.034 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:36.046
2025-07-02 05:48:36.056 >       yield from g
2025-07-02 05:48:36.064
2025-07-02 05:48:36.075 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:36.083 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:36.090
2025-07-02 05:48:36.103 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:36.114 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:36.122 alo = 202, ahi = 1101
2025-07-02 05:48:36.132 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:36.143 blo = 202, bhi = 1101
2025-07-02 05:48:36.151
2025-07-02 05:48:36.159 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:36.171 r"""
2025-07-02 05:48:36.182 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:36.190 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:36.197 synch point, and intraline difference marking is done on the
2025-07-02 05:48:36.205 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:36.212
2025-07-02 05:48:36.219 Example:
2025-07-02 05:48:36.226
2025-07-02 05:48:36.234 >>> d = Differ()
2025-07-02 05:48:36.246 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:36.256 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:36.264 >>> print(''.join(results), end="")
2025-07-02 05:48:36.271 - abcDefghiJkl
2025-07-02 05:48:36.284 + abcdefGhijkl
2025-07-02 05:48:36.297 """
2025-07-02 05:48:36.307
2025-07-02 05:48:36.315 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:36.322 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:36.329 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:36.341 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:36.351 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:36.367
2025-07-02 05:48:36.378 # search for the pair that matches best without being identical
2025-07-02 05:48:36.386 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:36.393 # on junk -- unless we have to)
2025-07-02 05:48:36.400 for j in range(blo, bhi):
2025-07-02 05:48:36.407 bj = b[j]
2025-07-02 05:48:36.414 cruncher.set_seq2(bj)
2025-07-02 05:48:36.425 for i in range(alo, ahi):
2025-07-02 05:48:36.434 ai = a[i]
2025-07-02 05:48:36.442 if ai == bj:
2025-07-02 05:48:36.448 if eqi is None:
2025-07-02 05:48:36.454 eqi, eqj = i, j
2025-07-02 05:48:36.460 continue
2025-07-02 05:48:36.466 cruncher.set_seq1(ai)
2025-07-02 05:48:36.473 # computing similarity is expensive, so use the quick
2025-07-02 05:48:36.479 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:36.487 # compares by a factor of 3.
2025-07-02 05:48:36.497 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:36.507 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:36.516 # of the computation is cached by cruncher
2025-07-02 05:48:36.531 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:36.543 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:36.552 cruncher.ratio() > best_ratio:
2025-07-02 05:48:36.560 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:36.567 if best_ratio < cutoff:
2025-07-02 05:48:36.574 # no non-identical "pretty close" pair
2025-07-02 05:48:36.586 if eqi is None:
2025-07-02 05:48:36.596 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:36.604 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:36.611 return
2025-07-02 05:48:36.618 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:36.625 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:36.631 else:
2025-07-02 05:48:36.637 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:36.643 eqi = None
2025-07-02 05:48:36.650
2025-07-02 05:48:36.662 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:36.672 # identical
2025-07-02 05:48:36.685
2025-07-02 05:48:36.694 # pump out diffs from before the synch point
2025-07-02 05:48:36.702 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:36.709
2025-07-02 05:48:36.715 # do intraline marking on the synch pair
2025-07-02 05:48:36.724 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:36.731 if eqi is None:
2025-07-02 05:48:36.738 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:36.746 atags = btags = ""
2025-07-02 05:48:36.754 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:36.765 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:36.777 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:36.786 if tag == 'replace':
2025-07-02 05:48:36.793 atags += '^' * la
2025-07-02 05:48:36.800 btags += '^' * lb
2025-07-02 05:48:36.808 elif tag == 'delete':
2025-07-02 05:48:36.814 atags += '-' * la
2025-07-02 05:48:36.824 elif tag == 'insert':
2025-07-02 05:48:36.831 btags += '+' * lb
2025-07-02 05:48:36.838 elif tag == 'equal':
2025-07-02 05:48:36.847 atags += ' ' * la
2025-07-02 05:48:36.858 btags += ' ' * lb
2025-07-02 05:48:36.867 else:
2025-07-02 05:48:36.875 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:36.883 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:36.889 else:
2025-07-02 05:48:36.895 # the synch pair is identical
2025-07-02 05:48:36.901 yield '  ' + aelt
2025-07-02 05:48:36.907
2025-07-02 05:48:36.913 # pump out diffs from after the synch point
2025-07-02 05:48:36.924 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:36.937
2025-07-02 05:48:36.950 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:36.963 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:36.976
2025-07-02 05:48:36.984 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:36.997 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:37.007 alo = 203, ahi = 1101
2025-07-02 05:48:37.015 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:37.025 blo = 203, bhi = 1101
2025-07-02 05:48:37.039
2025-07-02 05:48:37.047 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:37.055 g = []
2025-07-02 05:48:37.062 if alo < ahi:
2025-07-02 05:48:37.071 if blo < bhi:
2025-07-02 05:48:37.082 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:37.090 else:
2025-07-02 05:48:37.097 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:37.103 elif blo < bhi:
2025-07-02 05:48:37.115 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:37.125
2025-07-02 05:48:37.133 >       yield from g
2025-07-02 05:48:37.140
2025-07-02 05:48:37.148 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:37.155 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:37.163
2025-07-02 05:48:37.169 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:37.176 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:37.182 alo = 203, ahi = 1101
2025-07-02 05:48:37.194 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:37.204 blo = 203, bhi = 1101
2025-07-02 05:48:37.212
2025-07-02 05:48:37.218 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:37.224 r"""
2025-07-02 05:48:37.231 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:37.238 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:37.245 synch point, and intraline difference marking is done on the
2025-07-02 05:48:37.251 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:37.256
2025-07-02 05:48:37.261 Example:
2025-07-02 05:48:37.266
2025-07-02 05:48:37.271 >>> d = Differ()
2025-07-02 05:48:37.276 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:37.281 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:37.287 >>> print(''.join(results), end="")
2025-07-02 05:48:37.292 - abcDefghiJkl
2025-07-02 05:48:37.304 + abcdefGhijkl
2025-07-02 05:48:37.316 """
2025-07-02 05:48:37.324
2025-07-02 05:48:37.339 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:37.348 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:37.357 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:37.363 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:37.370 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:37.376
2025-07-02 05:48:37.381 # search for the pair that matches best without being identical
2025-07-02 05:48:37.387 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:37.393 # on junk -- unless we have to)
2025-07-02 05:48:37.399 for j in range(blo, bhi):
2025-07-02 05:48:37.405 bj = b[j]
2025-07-02 05:48:37.412 cruncher.set_seq2(bj)
2025-07-02 05:48:37.418 for i in range(alo, ahi):
2025-07-02 05:48:37.428 ai = a[i]
2025-07-02 05:48:37.438 if ai == bj:
2025-07-02 05:48:37.444 if eqi is None:
2025-07-02 05:48:37.452 eqi, eqj = i, j
2025-07-02 05:48:37.459 continue
2025-07-02 05:48:37.466 cruncher.set_seq1(ai)
2025-07-02 05:48:37.478 # computing similarity is expensive, so use the quick
2025-07-02 05:48:37.489 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:37.497 # compares by a factor of 3.
2025-07-02 05:48:37.504 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:37.510 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:37.515 # of the computation is cached by cruncher
2025-07-02 05:48:37.527 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:37.534 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:37.541 cruncher.ratio() > best_ratio:
2025-07-02 05:48:37.549 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:37.559 if best_ratio < cutoff:
2025-07-02 05:48:37.570 # no non-identical "pretty close" pair
2025-07-02 05:48:37.583 if eqi is None:
2025-07-02 05:48:37.596 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:37.607 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:37.619 return
2025-07-02 05:48:37.629 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:37.640 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:37.652 else:
2025-07-02 05:48:37.661 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:37.669 eqi = None
2025-07-02 05:48:37.675
2025-07-02 05:48:37.681 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:37.695 # identical
2025-07-02 05:48:37.709
2025-07-02 05:48:37.722 # pump out diffs from before the synch point
2025-07-02 05:48:37.731 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:37.737
2025-07-02 05:48:37.751 # do intraline marking on the synch pair
2025-07-02 05:48:37.766 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:37.776 if eqi is None:
2025-07-02 05:48:37.784 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:37.791 atags = btags = ""
2025-07-02 05:48:37.800 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:37.811 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:37.822 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:37.832 if tag == 'replace':
2025-07-02 05:48:37.840 atags += '^' * la
2025-07-02 05:48:37.849 btags += '^' * lb
2025-07-02 05:48:37.855 elif tag == 'delete':
2025-07-02 05:48:37.861 atags += '-' * la
2025-07-02 05:48:37.867 elif tag == 'insert':
2025-07-02 05:48:37.880 btags += '+' * lb
2025-07-02 05:48:37.890 elif tag == 'equal':
2025-07-02 05:48:37.901 atags += ' ' * la
2025-07-02 05:48:37.911 btags += ' ' * lb
2025-07-02 05:48:37.920 else:
2025-07-02 05:48:37.928 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:37.934 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:37.945 else:
2025-07-02 05:48:37.957 # the synch pair is identical
2025-07-02 05:48:37.965 yield '  ' + aelt
2025-07-02 05:48:37.973
2025-07-02 05:48:37.979 # pump out diffs from after the synch point
2025-07-02 05:48:37.987 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:37.997
2025-07-02 05:48:38.006 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:38.018 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:38.029
2025-07-02 05:48:38.038 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:38.046 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:38.055 alo = 204, ahi = 1101
2025-07-02 05:48:38.065 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:38.077 blo = 204, bhi = 1101
2025-07-02 05:48:38.089
2025-07-02 05:48:38.097 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:38.103 g = []
2025-07-02 05:48:38.109 if alo < ahi:
2025-07-02 05:48:38.115 if blo < bhi:
2025-07-02 05:48:38.122 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:38.128 else:
2025-07-02 05:48:38.136 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:38.141 elif blo < bhi:
2025-07-02 05:48:38.147 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:38.153
2025-07-02 05:48:38.159 >       yield from g
2025-07-02 05:48:38.173
2025-07-02 05:48:38.183 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:38.192 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:38.198
2025-07-02 05:48:38.204 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:38.218 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:38.232 alo = 204, ahi = 1101
2025-07-02 05:48:38.243 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:38.252 blo = 204, bhi = 1101
2025-07-02 05:48:38.259
2025-07-02 05:48:38.266 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:38.272 r"""
2025-07-02 05:48:38.279 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:38.286 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:38.292 synch point, and intraline difference marking is done on the
2025-07-02 05:48:38.301 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:38.312
2025-07-02 05:48:38.320 Example:
2025-07-02 05:48:38.328
2025-07-02 05:48:38.335 >>> d = Differ()
2025-07-02 05:48:38.341 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:38.347 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:38.353 >>> print(''.join(results), end="")
2025-07-02 05:48:38.359 - abcDefghiJkl
2025-07-02 05:48:38.378 + abcdefGhijkl
2025-07-02 05:48:38.403 """
2025-07-02 05:48:38.416
2025-07-02 05:48:38.425 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:38.432 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:38.439 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:38.444 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:38.449 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:38.455
2025-07-02 05:48:38.461 # search for the pair that matches best without being identical
2025-07-02 05:48:38.465 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:38.470 # on junk -- unless we have to)
2025-07-02 05:48:38.476 for j in range(blo, bhi):
2025-07-02 05:48:38.482 bj = b[j]
2025-07-02 05:48:38.489 cruncher.set_seq2(bj)
2025-07-02 05:48:38.494 for i in range(alo, ahi):
2025-07-02 05:48:38.500 ai = a[i]
2025-07-02 05:48:38.506 if ai == bj:
2025-07-02 05:48:38.511 if eqi is None:
2025-07-02 05:48:38.519 eqi, eqj = i, j
2025-07-02 05:48:38.532 continue
2025-07-02 05:48:38.543 cruncher.set_seq1(ai)
2025-07-02 05:48:38.552 # computing similarity is expensive, so use the quick
2025-07-02 05:48:38.560 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:38.569 # compares by a factor of 3.
2025-07-02 05:48:38.582 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:38.591 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:38.600 # of the computation is cached by cruncher
2025-07-02 05:48:38.608 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:38.614 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:38.625 cruncher.ratio() > best_ratio:
2025-07-02 05:48:38.635 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:38.643 if best_ratio < cutoff:
2025-07-02 05:48:38.651 # no non-identical "pretty close" pair
2025-07-02 05:48:38.659 if eqi is None:
2025-07-02 05:48:38.667 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:38.680 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:38.692 return
2025-07-02 05:48:38.702 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:38.712 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:38.721 else:
2025-07-02 05:48:38.732 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:38.742 eqi = None
2025-07-02 05:48:38.751
2025-07-02 05:48:38.759 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:38.771 # identical
2025-07-02 05:48:38.780
2025-07-02 05:48:38.787 # pump out diffs from before the synch point
2025-07-02 05:48:38.795 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:38.801
2025-07-02 05:48:38.807 # do intraline marking on the synch pair
2025-07-02 05:48:38.813 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:38.818 if eqi is None:
2025-07-02 05:48:38.825 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:38.830 atags = btags = ""
2025-07-02 05:48:38.841 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:38.853 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:38.862 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:38.872 if tag == 'replace':
2025-07-02 05:48:38.886 atags += '^' * la
2025-07-02 05:48:38.897 btags += '^' * lb
2025-07-02 05:48:38.910 elif tag == 'delete':
2025-07-02 05:48:38.920 atags += '-' * la
2025-07-02 05:48:38.932 elif tag == 'insert':
2025-07-02 05:48:38.941 btags += '+' * lb
2025-07-02 05:48:38.949 elif tag == 'equal':
2025-07-02 05:48:38.955 atags += ' ' * la
2025-07-02 05:48:38.960 btags += ' ' * lb
2025-07-02 05:48:38.966 else:
2025-07-02 05:48:38.971 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:38.976 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:38.981 else:
2025-07-02 05:48:38.986 # the synch pair is identical
2025-07-02 05:48:38.997 yield '  ' + aelt
2025-07-02 05:48:39.007
2025-07-02 05:48:39.017 # pump out diffs from after the synch point
2025-07-02 05:48:39.025 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:39.032
2025-07-02 05:48:39.039 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:39.046 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:39.057
2025-07-02 05:48:39.067 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:39.075 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:39.081 alo = 205, ahi = 1101
2025-07-02 05:48:39.094 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:39.103 blo = 205, bhi = 1101
2025-07-02 05:48:39.110
2025-07-02 05:48:39.117 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:39.123 g = []
2025-07-02 05:48:39.136 if alo < ahi:
2025-07-02 05:48:39.149 if blo < bhi:
2025-07-02 05:48:39.158 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:39.170 else:
2025-07-02 05:48:39.180 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:39.188 elif blo < bhi:
2025-07-02 05:48:39.195 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:39.208
2025-07-02 05:48:39.218 >       yield from g
2025-07-02 05:48:39.230
2025-07-02 05:48:39.241 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:39.250 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:39.259
2025-07-02 05:48:39.270 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:39.279 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:39.290 alo = 205, ahi = 1101
2025-07-02 05:48:39.302 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:39.311 blo = 205, bhi = 1101
2025-07-02 05:48:39.318
2025-07-02 05:48:39.326 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:39.336 r"""
2025-07-02 05:48:39.350 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:39.361 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:39.375 synch point, and intraline difference marking is done on the
2025-07-02 05:48:39.385 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:39.398
2025-07-02 05:48:39.410 Example:
2025-07-02 05:48:39.419
2025-07-02 05:48:39.428 >>> d = Differ()
2025-07-02 05:48:39.435 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:39.441 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:39.448 >>> print(''.join(results), end="")
2025-07-02 05:48:39.461 - abcDefghiJkl
2025-07-02 05:48:39.481 + abcdefGhijkl
2025-07-02 05:48:39.493 """
2025-07-02 05:48:39.499
2025-07-02 05:48:39.504 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:39.510 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:39.516 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:39.522 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:39.529 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:39.535
2025-07-02 05:48:39.542 # search for the pair that matches best without being identical
2025-07-02 05:48:39.549 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:39.562 # on junk -- unless we have to)
2025-07-02 05:48:39.573 for j in range(blo, bhi):
2025-07-02 05:48:39.587 bj = b[j]
2025-07-02 05:48:39.596 cruncher.set_seq2(bj)
2025-07-02 05:48:39.604 for i in range(alo, ahi):
2025-07-02 05:48:39.612 ai = a[i]
2025-07-02 05:48:39.618 if ai == bj:
2025-07-02 05:48:39.628 if eqi is None:
2025-07-02 05:48:39.635 eqi, eqj = i, j
2025-07-02 05:48:39.645 continue
2025-07-02 05:48:39.655 cruncher.set_seq1(ai)
2025-07-02 05:48:39.663 # computing similarity is expensive, so use the quick
2025-07-02 05:48:39.671 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:39.683 # compares by a factor of 3.
2025-07-02 05:48:39.694 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:39.701 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:39.707 # of the computation is cached by cruncher
2025-07-02 05:48:39.713 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:39.718 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:39.724 cruncher.ratio() > best_ratio:
2025-07-02 05:48:39.730 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:39.737 if best_ratio < cutoff:
2025-07-02 05:48:39.750 # no non-identical "pretty close" pair
2025-07-02 05:48:39.760 if eqi is None:
2025-07-02 05:48:39.768 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:39.775 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:39.782 return
2025-07-02 05:48:39.789 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:39.796 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:39.802 else:
2025-07-02 05:48:39.813 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:39.823 eqi = None
2025-07-02 05:48:39.831
2025-07-02 05:48:39.839 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:39.850 # identical
2025-07-02 05:48:39.859
2025-07-02 05:48:39.867 # pump out diffs from before the synch point
2025-07-02 05:48:39.874 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:39.883
2025-07-02 05:48:39.893 # do intraline marking on the synch pair
2025-07-02 05:48:39.901 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:39.908 if eqi is None:
2025-07-02 05:48:39.918 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:39.924 atags = btags = ""
2025-07-02 05:48:39.930 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:39.937 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:39.944 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:39.951 if tag == 'replace':
2025-07-02 05:48:39.957 atags += '^' * la
2025-07-02 05:48:39.964 btags += '^' * lb
2025-07-02 05:48:39.972 elif tag == 'delete':
2025-07-02 05:48:39.983 atags += '-' * la
2025-07-02 05:48:39.991 elif tag == 'insert':
2025-07-02 05:48:39.998 btags += '+' * lb
2025-07-02 05:48:40.006 elif tag == 'equal':
2025-07-02 05:48:40.017 atags += ' ' * la
2025-07-02 05:48:40.028 btags += ' ' * lb
2025-07-02 05:48:40.037 else:
2025-07-02 05:48:40.047 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:40.058 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:40.067 else:
2025-07-02 05:48:40.076 # the synch pair is identical
2025-07-02 05:48:40.082 yield '  ' + aelt
2025-07-02 05:48:40.088
2025-07-02 05:48:40.094 # pump out diffs from after the synch point
2025-07-02 05:48:40.106 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:40.115
2025-07-02 05:48:40.127 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:40.137 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:40.144
2025-07-02 05:48:40.157 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:40.168 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:40.175 alo = 206, ahi = 1101
2025-07-02 05:48:40.190 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:40.203 blo = 206, bhi = 1101
2025-07-02 05:48:40.212
2025-07-02 05:48:40.219 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:40.226 g = []
2025-07-02 05:48:40.231 if alo < ahi:
2025-07-02 05:48:40.236 if blo < bhi:
2025-07-02 05:48:40.241 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:40.247 else:
2025-07-02 05:48:40.253 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:40.259 elif blo < bhi:
2025-07-02 05:48:40.266 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:40.271
2025-07-02 05:48:40.279 >       yield from g
2025-07-02 05:48:40.290
2025-07-02 05:48:40.299 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:40.308 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:40.320
2025-07-02 05:48:40.330 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:40.338 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:40.345 alo = 206, ahi = 1101
2025-07-02 05:48:40.353 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:40.358 blo = 206, bhi = 1101
2025-07-02 05:48:40.370
2025-07-02 05:48:40.380 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:40.386 r"""
2025-07-02 05:48:40.399 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:40.412 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:40.423 synch point, and intraline difference marking is done on the
2025-07-02 05:48:40.431 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:40.443
2025-07-02 05:48:40.454 Example:
2025-07-02 05:48:40.466
2025-07-02 05:48:40.476 >>> d = Differ()
2025-07-02 05:48:40.486 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:40.498 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:40.509 >>> print(''.join(results), end="")
2025-07-02 05:48:40.517 - abcDefghiJkl
2025-07-02 05:48:40.534 + abcdefGhijkl
2025-07-02 05:48:40.557 """
2025-07-02 05:48:40.568
2025-07-02 05:48:40.583 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:40.593 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:40.606 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:40.618 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:40.630 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:40.639
2025-07-02 05:48:40.649 # search for the pair that matches best without being identical
2025-07-02 05:48:40.663 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:40.675 # on junk -- unless we have to)
2025-07-02 05:48:40.687 for j in range(blo, bhi):
2025-07-02 05:48:40.696 bj = b[j]
2025-07-02 05:48:40.704 cruncher.set_seq2(bj)
2025-07-02 05:48:40.711 for i in range(alo, ahi):
2025-07-02 05:48:40.721 ai = a[i]
2025-07-02 05:48:40.731 if ai == bj:
2025-07-02 05:48:40.737 if eqi is None:
2025-07-02 05:48:40.743 eqi, eqj = i, j
2025-07-02 05:48:40.747 continue
2025-07-02 05:48:40.755 cruncher.set_seq1(ai)
2025-07-02 05:48:40.767 # computing similarity is expensive, so use the quick
2025-07-02 05:48:40.778 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:40.788 # compares by a factor of 3.
2025-07-02 05:48:40.796 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:40.809 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:40.821 # of the computation is cached by cruncher
2025-07-02 05:48:40.829 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:40.837 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:40.843 cruncher.ratio() > best_ratio:
2025-07-02 05:48:40.849 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:40.853 if best_ratio < cutoff:
2025-07-02 05:48:40.859 # no non-identical "pretty close" pair
2025-07-02 05:48:40.865 if eqi is None:
2025-07-02 05:48:40.871 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:40.880 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:40.892 return
2025-07-02 05:48:40.899 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:40.908 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:40.921 else:
2025-07-02 05:48:40.931 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:40.939 eqi = None
2025-07-02 05:48:40.946
2025-07-02 05:48:40.953 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:40.958 # identical
2025-07-02 05:48:40.964
2025-07-02 05:48:40.970 # pump out diffs from before the synch point
2025-07-02 05:48:40.976 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:40.981
2025-07-02 05:48:40.988 # do intraline marking on the synch pair
2025-07-02 05:48:40.994 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:41.000 if eqi is None:
2025-07-02 05:48:41.006 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:41.011 atags = btags = ""
2025-07-02 05:48:41.019 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:41.031 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:41.041 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:41.050 if tag == 'replace':
2025-07-02 05:48:41.057 atags += '^' * la
2025-07-02 05:48:41.063 btags += '^' * lb
2025-07-02 05:48:41.069 elif tag == 'delete':
2025-07-02 05:48:41.082 atags += '-' * la
2025-07-02 05:48:41.091 elif tag == 'insert':
2025-07-02 05:48:41.099 btags += '+' * lb
2025-07-02 05:48:41.110 elif tag == 'equal':
2025-07-02 05:48:41.120 atags += ' ' * la
2025-07-02 05:48:41.128 btags += ' ' * lb
2025-07-02 05:48:41.135 else:
2025-07-02 05:48:41.142 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:41.151 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:41.163 else:
2025-07-02 05:48:41.172 # the synch pair is identical
2025-07-02 05:48:41.179 yield '  ' + aelt
2025-07-02 05:48:41.185
2025-07-02 05:48:41.191 # pump out diffs from after the synch point
2025-07-02 05:48:41.197 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:41.203
2025-07-02 05:48:41.209 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:41.215 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:41.223
2025-07-02 05:48:41.235 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:41.244 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:41.253 alo = 207, ahi = 1101
2025-07-02 05:48:41.262 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:41.269 blo = 207, bhi = 1101
2025-07-02 05:48:41.275
2025-07-02 05:48:41.287 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:41.298 g = []
2025-07-02 05:48:41.310 if alo < ahi:
2025-07-02 05:48:41.324 if blo < bhi:
2025-07-02 05:48:41.334 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:41.342 else:
2025-07-02 05:48:41.351 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:41.363 elif blo < bhi:
2025-07-02 05:48:41.373 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:41.381
2025-07-02 05:48:41.387 >       yield from g
2025-07-02 05:48:41.394
2025-07-02 05:48:41.399 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:41.407 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:41.416
2025-07-02 05:48:41.428 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:41.439 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:41.449 alo = 207, ahi = 1101
2025-07-02 05:48:41.459 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:41.466 blo = 207, bhi = 1101
2025-07-02 05:48:41.473
2025-07-02 05:48:41.479 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:41.485 r"""
2025-07-02 05:48:41.492 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:41.498 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:41.504 synch point, and intraline difference marking is done on the
2025-07-02 05:48:41.510 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:41.516
2025-07-02 05:48:41.522 Example:
2025-07-02 05:48:41.534
2025-07-02 05:48:41.544 >>> d = Differ()
2025-07-02 05:48:41.552 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:41.560 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:41.566 >>> print(''.join(results), end="")
2025-07-02 05:48:41.573 - abcDefghiJkl
2025-07-02 05:48:41.587 + abcdefGhijkl
2025-07-02 05:48:41.608 """
2025-07-02 05:48:41.616
2025-07-02 05:48:41.622 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:41.633 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:41.643 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:41.651 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:41.665 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:41.675
2025-07-02 05:48:41.682 # search for the pair that matches best without being identical
2025-07-02 05:48:41.689 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:41.701 # on junk -- unless we have to)
2025-07-02 05:48:41.714 for j in range(blo, bhi):
2025-07-02 05:48:41.726 bj = b[j]
2025-07-02 05:48:41.736 cruncher.set_seq2(bj)
2025-07-02 05:48:41.744 for i in range(alo, ahi):
2025-07-02 05:48:41.757 ai = a[i]
2025-07-02 05:48:41.770 if ai == bj:
2025-07-02 05:48:41.782 if eqi is None:
2025-07-02 05:48:41.791 eqi, eqj = i, j
2025-07-02 05:48:41.800 continue
2025-07-02 05:48:41.807 cruncher.set_seq1(ai)
2025-07-02 05:48:41.813 # computing similarity is expensive, so use the quick
2025-07-02 05:48:41.818 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:41.823 # compares by a factor of 3.
2025-07-02 05:48:41.827 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:41.832 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:41.837 # of the computation is cached by cruncher
2025-07-02 05:48:41.841 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:41.846 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:41.850 cruncher.ratio() > best_ratio:
2025-07-02 05:48:41.855 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:41.859 if best_ratio < cutoff:
2025-07-02 05:48:41.864 # no non-identical "pretty close" pair
2025-07-02 05:48:41.868 if eqi is None:
2025-07-02 05:48:41.873 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:41.877 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:41.882 return
2025-07-02 05:48:41.886 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:41.891 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:41.896 else:
2025-07-02 05:48:41.900 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:41.905 eqi = None
2025-07-02 05:48:41.909
2025-07-02 05:48:41.913 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:41.918 # identical
2025-07-02 05:48:41.922
2025-07-02 05:48:41.928 # pump out diffs from before the synch point
2025-07-02 05:48:41.932 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:41.937
2025-07-02 05:48:41.941 # do intraline marking on the synch pair
2025-07-02 05:48:41.946 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:41.950 if eqi is None:
2025-07-02 05:48:41.955 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:41.959 atags = btags = ""
2025-07-02 05:48:41.963 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:41.968 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:41.972 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:41.977 if tag == 'replace':
2025-07-02 05:48:41.981 atags += '^' * la
2025-07-02 05:48:41.986 btags += '^' * lb
2025-07-02 05:48:41.990 elif tag == 'delete':
2025-07-02 05:48:41.995 atags += '-' * la
2025-07-02 05:48:41.999 elif tag == 'insert':
2025-07-02 05:48:42.004 btags += '+' * lb
2025-07-02 05:48:42.009 elif tag == 'equal':
2025-07-02 05:48:42.014 atags += ' ' * la
2025-07-02 05:48:42.018 btags += ' ' * lb
2025-07-02 05:48:42.023 else:
2025-07-02 05:48:42.027 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:42.032 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:42.038 else:
2025-07-02 05:48:42.042 # the synch pair is identical
2025-07-02 05:48:42.046 yield '  ' + aelt
2025-07-02 05:48:42.051
2025-07-02 05:48:42.055 # pump out diffs from after the synch point
2025-07-02 05:48:42.059 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:42.064
2025-07-02 05:48:42.068 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:42.073 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:42.077
2025-07-02 05:48:42.082 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:42.087 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:42.091 alo = 208, ahi = 1101
2025-07-02 05:48:42.096 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:42.100 blo = 208, bhi = 1101
2025-07-02 05:48:42.105
2025-07-02 05:48:42.109 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:42.113 g = []
2025-07-02 05:48:42.118 if alo < ahi:
2025-07-02 05:48:42.122 if blo < bhi:
2025-07-02 05:48:42.127 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:42.131 else:
2025-07-02 05:48:42.135 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:42.140 elif blo < bhi:
2025-07-02 05:48:42.144 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:42.148
2025-07-02 05:48:42.153 >       yield from g
2025-07-02 05:48:42.157
2025-07-02 05:48:42.162 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:42.166 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:42.171
2025-07-02 05:48:42.175 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:42.180 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:42.184 alo = 208, ahi = 1101
2025-07-02 05:48:42.193 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:42.199 blo = 208, bhi = 1101
2025-07-02 05:48:42.209
2025-07-02 05:48:42.219 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:42.227 r"""
2025-07-02 05:48:42.234 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:42.241 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:42.247 synch point, and intraline difference marking is done on the
2025-07-02 05:48:42.254 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:42.259
2025-07-02 05:48:42.267 Example:
2025-07-02 05:48:42.278
2025-07-02 05:48:42.287 >>> d = Differ()
2025-07-02 05:48:42.294 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:42.299 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:42.304 >>> print(''.join(results), end="")
2025-07-02 05:48:42.308 - abcDefghiJkl
2025-07-02 05:48:42.317 + abcdefGhijkl
2025-07-02 05:48:42.326 """
2025-07-02 05:48:42.330
2025-07-02 05:48:42.337 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:42.342 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:42.348 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:42.354 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:42.365 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:42.374
2025-07-02 05:48:42.382 # search for the pair that matches best without being identical
2025-07-02 05:48:42.391 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:42.399 # on junk -- unless we have to)
2025-07-02 05:48:42.412 for j in range(blo, bhi):
2025-07-02 05:48:42.419 bj = b[j]
2025-07-02 05:48:42.426 cruncher.set_seq2(bj)
2025-07-02 05:48:42.432 for i in range(alo, ahi):
2025-07-02 05:48:42.441 ai = a[i]
2025-07-02 05:48:42.453 if ai == bj:
2025-07-02 05:48:42.464 if eqi is None:
2025-07-02 05:48:42.472 eqi, eqj = i, j
2025-07-02 05:48:42.479 continue
2025-07-02 05:48:42.486 cruncher.set_seq1(ai)
2025-07-02 05:48:42.497 # computing similarity is expensive, so use the quick
2025-07-02 05:48:42.509 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:42.521 # compares by a factor of 3.
2025-07-02 05:48:42.533 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:42.545 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:42.555 # of the computation is cached by cruncher
2025-07-02 05:48:42.563 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:42.570 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:42.584 cruncher.ratio() > best_ratio:
2025-07-02 05:48:42.595 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:42.605 if best_ratio < cutoff:
2025-07-02 05:48:42.619 # no non-identical "pretty close" pair
2025-07-02 05:48:42.629 if eqi is None:
2025-07-02 05:48:42.637 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:42.643 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:42.654 return
2025-07-02 05:48:42.665 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:42.677 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:42.687 else:
2025-07-02 05:48:42.694 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:42.701 eqi = None
2025-07-02 05:48:42.708
2025-07-02 05:48:42.720 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:42.731 # identical
2025-07-02 05:48:42.741
2025-07-02 05:48:42.748 # pump out diffs from before the synch point
2025-07-02 05:48:42.754 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:42.760
2025-07-02 05:48:42.766 # do intraline marking on the synch pair
2025-07-02 05:48:42.774 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:42.780 if eqi is None:
2025-07-02 05:48:42.787 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:42.795 atags = btags = ""
2025-07-02 05:48:42.806 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:42.815 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:42.822 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:42.828 if tag == 'replace':
2025-07-02 05:48:42.833 atags += '^' * la
2025-07-02 05:48:42.838 btags += '^' * lb
2025-07-02 05:48:42.843 elif tag == 'delete':
2025-07-02 05:48:42.855 atags += '-' * la
2025-07-02 05:48:42.863 elif tag == 'insert':
2025-07-02 05:48:42.871 btags += '+' * lb
2025-07-02 05:48:42.878 elif tag == 'equal':
2025-07-02 05:48:42.890 atags += ' ' * la
2025-07-02 05:48:42.899 btags += ' ' * lb
2025-07-02 05:48:42.907 else:
2025-07-02 05:48:42.914 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:42.922 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:42.929 else:
2025-07-02 05:48:42.936 # the synch pair is identical
2025-07-02 05:48:42.942 yield '  ' + aelt
2025-07-02 05:48:42.949
2025-07-02 05:48:42.955 # pump out diffs from after the synch point
2025-07-02 05:48:42.962 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:42.974
2025-07-02 05:48:42.984 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:42.998 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:43.009
2025-07-02 05:48:43.019 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:43.032 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:43.040 alo = 209, ahi = 1101
2025-07-02 05:48:43.050 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:43.057 blo = 209, bhi = 1101
2025-07-02 05:48:43.066
2025-07-02 05:48:43.074 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:43.081 g = []
2025-07-02 05:48:43.087 if alo < ahi:
2025-07-02 05:48:43.093 if blo < bhi:
2025-07-02 05:48:43.098 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:43.103 else:
2025-07-02 05:48:43.108 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:43.112 elif blo < bhi:
2025-07-02 05:48:43.117 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:43.123
2025-07-02 05:48:43.128 >       yield from g
2025-07-02 05:48:43.133
2025-07-02 05:48:43.137 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:43.143 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:43.147
2025-07-02 05:48:43.152 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:43.159 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:43.170 alo = 209, ahi = 1101
2025-07-02 05:48:43.181 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:43.189 blo = 209, bhi = 1101
2025-07-02 05:48:43.196
2025-07-02 05:48:43.204 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:43.211 r"""
2025-07-02 05:48:43.217 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:43.224 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:43.230 synch point, and intraline difference marking is done on the
2025-07-02 05:48:43.236 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:43.242
2025-07-02 05:48:43.247 Example:
2025-07-02 05:48:43.253
2025-07-02 05:48:43.258 >>> d = Differ()
2025-07-02 05:48:43.264 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:43.270 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:43.276 >>> print(''.join(results), end="")
2025-07-02 05:48:43.282 - abcDefghiJkl
2025-07-02 05:48:43.301 + abcdefGhijkl
2025-07-02 05:48:43.316 """
2025-07-02 05:48:43.322
2025-07-02 05:48:43.329 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:43.342 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:43.353 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:43.365 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:43.376 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:43.384
2025-07-02 05:48:43.390 # search for the pair that matches best without being identical
2025-07-02 05:48:43.396 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:43.402 # on junk -- unless we have to)
2025-07-02 05:48:43.409 for j in range(blo, bhi):
2025-07-02 05:48:43.416 bj = b[j]
2025-07-02 05:48:43.423 cruncher.set_seq2(bj)
2025-07-02 05:48:43.436 for i in range(alo, ahi):
2025-07-02 05:48:43.444 ai = a[i]
2025-07-02 05:48:43.452 if ai == bj:
2025-07-02 05:48:43.464 if eqi is None:
2025-07-02 05:48:43.472 eqi, eqj = i, j
2025-07-02 05:48:43.479 continue
2025-07-02 05:48:43.487 cruncher.set_seq1(ai)
2025-07-02 05:48:43.499 # computing similarity is expensive, so use the quick
2025-07-02 05:48:43.507 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:43.515 # compares by a factor of 3.
2025-07-02 05:48:43.524 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:43.532 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:43.546 # of the computation is cached by cruncher
2025-07-02 05:48:43.556 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:43.564 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:43.571 cruncher.ratio() > best_ratio:
2025-07-02 05:48:43.578 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:43.587 if best_ratio < cutoff:
2025-07-02 05:48:43.593 # no non-identical "pretty close" pair
2025-07-02 05:48:43.599 if eqi is None:
2025-07-02 05:48:43.606 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:43.612 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:43.626 return
2025-07-02 05:48:43.637 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:43.644 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:43.650 else:
2025-07-02 05:48:43.659 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:43.670 eqi = None
2025-07-02 05:48:43.678
2025-07-02 05:48:43.688 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:43.695 # identical
2025-07-02 05:48:43.703
2025-07-02 05:48:43.712 # pump out diffs from before the synch point
2025-07-02 05:48:43.723 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:43.731
2025-07-02 05:48:43.737 # do intraline marking on the synch pair
2025-07-02 05:48:43.743 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:43.752 if eqi is None:
2025-07-02 05:48:43.758 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:43.764 atags = btags = ""
2025-07-02 05:48:43.770 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:43.775 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:43.781 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:43.787 if tag == 'replace':
2025-07-02 05:48:43.794 atags += '^' * la
2025-07-02 05:48:43.800 btags += '^' * lb
2025-07-02 05:48:43.807 elif tag == 'delete':
2025-07-02 05:48:43.820 atags += '-' * la
2025-07-02 05:48:43.831 elif tag == 'insert':
2025-07-02 05:48:43.842 btags += '+' * lb
2025-07-02 05:48:43.853 elif tag == 'equal':
2025-07-02 05:48:43.865 atags += ' ' * la
2025-07-02 05:48:43.877 btags += ' ' * lb
2025-07-02 05:48:43.887 else:
2025-07-02 05:48:43.896 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:43.904 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:43.910 else:
2025-07-02 05:48:43.917 # the synch pair is identical
2025-07-02 05:48:43.923 yield '  ' + aelt
2025-07-02 05:48:43.928
2025-07-02 05:48:43.935 # pump out diffs from after the synch point
2025-07-02 05:48:43.943 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:43.954
2025-07-02 05:48:43.962 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:43.973 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:43.985
2025-07-02 05:48:43.997 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:44.007 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:44.014 alo = 210, ahi = 1101
2025-07-02 05:48:44.021 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:44.027 blo = 210, bhi = 1101
2025-07-02 05:48:44.035
2025-07-02 05:48:44.049 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:44.062 g = []
2025-07-02 05:48:44.076 if alo < ahi:
2025-07-02 05:48:44.088 if blo < bhi:
2025-07-02 05:48:44.097 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:44.104 else:
2025-07-02 05:48:44.118 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:44.131 elif blo < bhi:
2025-07-02 05:48:44.144 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:44.157
2025-07-02 05:48:44.168 >       yield from g
2025-07-02 05:48:44.179
2025-07-02 05:48:44.189 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:44.201 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:44.211
2025-07-02 05:48:44.219 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:44.228 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:44.234 alo = 210, ahi = 1101
2025-07-02 05:48:44.243 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:44.255 blo = 210, bhi = 1101
2025-07-02 05:48:44.270
2025-07-02 05:48:44.282 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:44.292 r"""
2025-07-02 05:48:44.300 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:44.307 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:44.314 synch point, and intraline difference marking is done on the
2025-07-02 05:48:44.325 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:44.337
2025-07-02 05:48:44.346 Example:
2025-07-02 05:48:44.356
2025-07-02 05:48:44.363 >>> d = Differ()
2025-07-02 05:48:44.370 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:44.382 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:44.392 >>> print(''.join(results), end="")
2025-07-02 05:48:44.403 - abcDefghiJkl
2025-07-02 05:48:44.420 + abcdefGhijkl
2025-07-02 05:48:44.435 """
2025-07-02 05:48:44.446
2025-07-02 05:48:44.457 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:44.464 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:44.471 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:44.477 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:44.484 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:44.496
2025-07-02 05:48:44.508 # search for the pair that matches best without being identical
2025-07-02 05:48:44.520 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:44.530 # on junk -- unless we have to)
2025-07-02 05:48:44.538 for j in range(blo, bhi):
2025-07-02 05:48:44.549 bj = b[j]
2025-07-02 05:48:44.560 cruncher.set_seq2(bj)
2025-07-02 05:48:44.572 for i in range(alo, ahi):
2025-07-02 05:48:44.584 ai = a[i]
2025-07-02 05:48:44.592 if ai == bj:
2025-07-02 05:48:44.599 if eqi is None:
2025-07-02 05:48:44.607 eqi, eqj = i, j
2025-07-02 05:48:44.613 continue
2025-07-02 05:48:44.620 cruncher.set_seq1(ai)
2025-07-02 05:48:44.626 # computing similarity is expensive, so use the quick
2025-07-02 05:48:44.637 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:44.646 # compares by a factor of 3.
2025-07-02 05:48:44.657 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:44.671 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:44.680 # of the computation is cached by cruncher
2025-07-02 05:48:44.689 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:44.694 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:44.703 cruncher.ratio() > best_ratio:
2025-07-02 05:48:44.715 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:44.724 if best_ratio < cutoff:
2025-07-02 05:48:44.734 # no non-identical "pretty close" pair
2025-07-02 05:48:44.746 if eqi is None:
2025-07-02 05:48:44.757 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:44.765 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:44.777 return
2025-07-02 05:48:44.787 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:44.799 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:44.809 else:
2025-07-02 05:48:44.820 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:44.831 eqi = None
2025-07-02 05:48:44.840
2025-07-02 05:48:44.847 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:44.854 # identical
2025-07-02 05:48:44.860
2025-07-02 05:48:44.867 # pump out diffs from before the synch point
2025-07-02 05:48:44.874 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:44.884
2025-07-02 05:48:44.894 # do intraline marking on the synch pair
2025-07-02 05:48:44.902 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:44.911 if eqi is None:
2025-07-02 05:48:44.924 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:44.935 atags = btags = ""
2025-07-02 05:48:44.945 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:44.958 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:44.969 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:44.979 if tag == 'replace':
2025-07-02 05:48:44.987 atags += '^' * la
2025-07-02 05:48:44.995 btags += '^' * lb
2025-07-02 05:48:45.002 elif tag == 'delete':
2025-07-02 05:48:45.009 atags += '-' * la
2025-07-02 05:48:45.015 elif tag == 'insert':
2025-07-02 05:48:45.023 btags += '+' * lb
2025-07-02 05:48:45.035 elif tag == 'equal':
2025-07-02 05:48:45.045 atags += ' ' * la
2025-07-02 05:48:45.052 btags += ' ' * lb
2025-07-02 05:48:45.059 else:
2025-07-02 05:48:45.066 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:45.075 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:45.087 else:
2025-07-02 05:48:45.095 # the synch pair is identical
2025-07-02 05:48:45.105 yield '  ' + aelt
2025-07-02 05:48:45.117
2025-07-02 05:48:45.127 # pump out diffs from after the synch point
2025-07-02 05:48:45.142 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:45.151
2025-07-02 05:48:45.159 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:45.165 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:45.172
2025-07-02 05:48:45.179 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:45.187 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:45.193 alo = 211, ahi = 1101
2025-07-02 05:48:45.200 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:45.206 blo = 211, bhi = 1101
2025-07-02 05:48:45.212
2025-07-02 05:48:45.218 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:45.229 g = []
2025-07-02 05:48:45.239 if alo < ahi:
2025-07-02 05:48:45.248 if blo < bhi:
2025-07-02 05:48:45.259 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:45.268 else:
2025-07-02 05:48:45.276 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:45.282 elif blo < bhi:
2025-07-02 05:48:45.295 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:45.305
2025-07-02 05:48:45.314 >       yield from g
2025-07-02 05:48:45.322
2025-07-02 05:48:45.329 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:45.336 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:45.343
2025-07-02 05:48:45.352 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:45.364 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:45.377 alo = 211, ahi = 1101
2025-07-02 05:48:45.389 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:45.399 blo = 211, bhi = 1101
2025-07-02 05:48:45.409
2025-07-02 05:48:45.421 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:45.430 r"""
2025-07-02 05:48:45.439 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:45.447 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:45.455 synch point, and intraline difference marking is done on the
2025-07-02 05:48:45.463 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:45.473
2025-07-02 05:48:45.481 Example:
2025-07-02 05:48:45.488
2025-07-02 05:48:45.494 >>> d = Differ()
2025-07-02 05:48:45.500 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:45.514 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:45.525 >>> print(''.join(results), end="")
2025-07-02 05:48:45.536 - abcDefghiJkl
2025-07-02 05:48:45.559 + abcdefGhijkl
2025-07-02 05:48:45.581 """
2025-07-02 05:48:45.589
2025-07-02 05:48:45.601 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:45.611 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:45.619 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:45.628 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:45.639 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:45.648
2025-07-02 05:48:45.657 # search for the pair that matches best without being identical
2025-07-02 05:48:45.664 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:45.670 # on junk -- unless we have to)
2025-07-02 05:48:45.676 for j in range(blo, bhi):
2025-07-02 05:48:45.686 bj = b[j]
2025-07-02 05:48:45.697 cruncher.set_seq2(bj)
2025-07-02 05:48:45.706 for i in range(alo, ahi):
2025-07-02 05:48:45.714 ai = a[i]
2025-07-02 05:48:45.724 if ai == bj:
2025-07-02 05:48:45.734 if eqi is None:
2025-07-02 05:48:45.744 eqi, eqj = i, j
2025-07-02 05:48:45.757 continue
2025-07-02 05:48:45.768 cruncher.set_seq1(ai)
2025-07-02 05:48:45.777 # computing similarity is expensive, so use the quick
2025-07-02 05:48:45.784 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:45.792 # compares by a factor of 3.
2025-07-02 05:48:45.799 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:45.806 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:45.813 # of the computation is cached by cruncher
2025-07-02 05:48:45.819 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:45.826 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:45.833 cruncher.ratio() > best_ratio:
2025-07-02 05:48:45.839 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:45.844 if best_ratio < cutoff:
2025-07-02 05:48:45.850 # no non-identical "pretty close" pair
2025-07-02 05:48:45.856 if eqi is None:
2025-07-02 05:48:45.863 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:45.877 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:45.890 return
2025-07-02 05:48:45.901 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:45.913 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:45.925 else:
2025-07-02 05:48:45.933 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:45.941 eqi = None
2025-07-02 05:48:45.948
2025-07-02 05:48:45.955 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:45.961 # identical
2025-07-02 05:48:45.966
2025-07-02 05:48:45.972 # pump out diffs from before the synch point
2025-07-02 05:48:45.979 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:45.985
2025-07-02 05:48:45.997 # do intraline marking on the synch pair
2025-07-02 05:48:46.009 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:46.017 if eqi is None:
2025-07-02 05:48:46.026 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:46.031 atags = btags = ""
2025-07-02 05:48:46.037 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:46.043 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:46.051 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:46.061 if tag == 'replace':
2025-07-02 05:48:46.069 atags += '^' * la
2025-07-02 05:48:46.076 btags += '^' * lb
2025-07-02 05:48:46.082 elif tag == 'delete':
2025-07-02 05:48:46.088 atags += '-' * la
2025-07-02 05:48:46.094 elif tag == 'insert':
2025-07-02 05:48:46.105 btags += '+' * lb
2025-07-02 05:48:46.114 elif tag == 'equal':
2025-07-02 05:48:46.121 atags += ' ' * la
2025-07-02 05:48:46.128 btags += ' ' * lb
2025-07-02 05:48:46.140 else:
2025-07-02 05:48:46.149 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:46.160 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:46.170 else:
2025-07-02 05:48:46.180 # the synch pair is identical
2025-07-02 05:48:46.192 yield '  ' + aelt
2025-07-02 05:48:46.202
2025-07-02 05:48:46.213 # pump out diffs from after the synch point
2025-07-02 05:48:46.223 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:46.230
2025-07-02 05:48:46.241 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:46.255 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:46.267
2025-07-02 05:48:46.276 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:46.291 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:46.301 alo = 212, ahi = 1101
2025-07-02 05:48:46.313 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:46.322 blo = 212, bhi = 1101
2025-07-02 05:48:46.333
2025-07-02 05:48:46.346 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:46.357 g = []
2025-07-02 05:48:46.370 if alo < ahi:
2025-07-02 05:48:46.383 if blo < bhi:
2025-07-02 05:48:46.391 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:46.402 else:
2025-07-02 05:48:46.415 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:46.426 elif blo < bhi:
2025-07-02 05:48:46.438 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:46.452
2025-07-02 05:48:46.461 >       yield from g
2025-07-02 05:48:46.468
2025-07-02 05:48:46.475 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:46.482 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:46.492
2025-07-02 05:48:46.500 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:46.510 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:46.520 alo = 212, ahi = 1101
2025-07-02 05:48:46.532 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:46.543 blo = 212, bhi = 1101
2025-07-02 05:48:46.555
2025-07-02 05:48:46.566 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:46.577 r"""
2025-07-02 05:48:46.588 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:46.597 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:46.605 synch point, and intraline difference marking is done on the
2025-07-02 05:48:46.615 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:46.627
2025-07-02 05:48:46.637 Example:
2025-07-02 05:48:46.644
2025-07-02 05:48:46.651 >>> d = Differ()
2025-07-02 05:48:46.657 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:46.662 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:46.668 >>> print(''.join(results), end="")
2025-07-02 05:48:46.675 - abcDefghiJkl
2025-07-02 05:48:46.698 + abcdefGhijkl
2025-07-02 05:48:46.717 """
2025-07-02 05:48:46.727
2025-07-02 05:48:46.738 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:46.747 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:46.755 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:46.763 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:46.774 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:46.782
2025-07-02 05:48:46.790 # search for the pair that matches best without being identical
2025-07-02 05:48:46.798 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:46.810 # on junk -- unless we have to)
2025-07-02 05:48:46.820 for j in range(blo, bhi):
2025-07-02 05:48:46.832 bj = b[j]
2025-07-02 05:48:46.843 cruncher.set_seq2(bj)
2025-07-02 05:48:46.851 for i in range(alo, ahi):
2025-07-02 05:48:46.859 ai = a[i]
2025-07-02 05:48:46.869 if ai == bj:
2025-07-02 05:48:46.877 if eqi is None:
2025-07-02 05:48:46.884 eqi, eqj = i, j
2025-07-02 05:48:46.890 continue
2025-07-02 05:48:46.902 cruncher.set_seq1(ai)
2025-07-02 05:48:46.912 # computing similarity is expensive, so use the quick
2025-07-02 05:48:46.920 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:46.926 # compares by a factor of 3.
2025-07-02 05:48:46.932 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:46.939 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:46.945 # of the computation is cached by cruncher
2025-07-02 05:48:46.955 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:46.963 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:46.970 cruncher.ratio() > best_ratio:
2025-07-02 05:48:46.976 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:46.982 if best_ratio < cutoff:
2025-07-02 05:48:46.990 # no non-identical "pretty close" pair
2025-07-02 05:48:47.001 if eqi is None:
2025-07-02 05:48:47.010 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:47.021 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:47.031 return
2025-07-02 05:48:47.044 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:47.056 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:47.065 else:
2025-07-02 05:48:47.072 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:47.078 eqi = None
2025-07-02 05:48:47.083
2025-07-02 05:48:47.088 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:47.093 # identical
2025-07-02 05:48:47.098
2025-07-02 05:48:47.104 # pump out diffs from before the synch point
2025-07-02 05:48:47.109 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:47.117
2025-07-02 05:48:47.125 # do intraline marking on the synch pair
2025-07-02 05:48:47.132 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:47.139 if eqi is None:
2025-07-02 05:48:47.146 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:47.157 atags = btags = ""
2025-07-02 05:48:47.166 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:47.173 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:47.179 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:47.185 if tag == 'replace':
2025-07-02 05:48:47.191 atags += '^' * la
2025-07-02 05:48:47.197 btags += '^' * lb
2025-07-02 05:48:47.203 elif tag == 'delete':
2025-07-02 05:48:47.210 atags += '-' * la
2025-07-02 05:48:47.217 elif tag == 'insert':
2025-07-02 05:48:47.224 btags += '+' * lb
2025-07-02 05:48:47.231 elif tag == 'equal':
2025-07-02 05:48:47.238 atags += ' ' * la
2025-07-02 05:48:47.247 btags += ' ' * lb
2025-07-02 05:48:47.259 else:
2025-07-02 05:48:47.268 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:47.275 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:47.282 else:
2025-07-02 05:48:47.288 # the synch pair is identical
2025-07-02 05:48:47.294 yield '  ' + aelt
2025-07-02 05:48:47.299
2025-07-02 05:48:47.306 # pump out diffs from after the synch point
2025-07-02 05:48:47.313 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:47.319
2025-07-02 05:48:47.324 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:47.331 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:47.336
2025-07-02 05:48:47.342 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:47.352 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:47.357 alo = 213, ahi = 1101
2025-07-02 05:48:47.364 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:47.370 blo = 213, bhi = 1101
2025-07-02 05:48:47.375
2025-07-02 05:48:47.382 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:47.394 g = []
2025-07-02 05:48:47.403 if alo < ahi:
2025-07-02 05:48:47.411 if blo < bhi:
2025-07-02 05:48:47.418 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:47.429 else:
2025-07-02 05:48:47.439 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:47.452 elif blo < bhi:
2025-07-02 05:48:47.465 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:47.478
2025-07-02 05:48:47.489 >       yield from g
2025-07-02 05:48:47.497
2025-07-02 05:48:47.507 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:47.519 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:47.529
2025-07-02 05:48:47.537 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:47.545 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:47.552 alo = 213, ahi = 1101
2025-07-02 05:48:47.559 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:47.567 blo = 213, bhi = 1101
2025-07-02 05:48:47.578
2025-07-02 05:48:47.587 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:47.594 r"""
2025-07-02 05:48:47.599 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:47.604 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:47.610 synch point, and intraline difference marking is done on the
2025-07-02 05:48:47.615 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:47.620
2025-07-02 05:48:47.625 Example:
2025-07-02 05:48:47.630
2025-07-02 05:48:47.639 >>> d = Differ()
2025-07-02 05:48:47.647 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:47.654 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:47.661 >>> print(''.join(results), end="")
2025-07-02 05:48:47.666 - abcDefghiJkl
2025-07-02 05:48:47.681 + abcdefGhijkl
2025-07-02 05:48:47.697 """
2025-07-02 05:48:47.709
2025-07-02 05:48:47.723 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:47.733 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:47.745 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:47.755 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:47.763 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:47.771
2025-07-02 05:48:47.779 # search for the pair that matches best without being identical
2025-07-02 05:48:47.790 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:47.798 # on junk -- unless we have to)
2025-07-02 05:48:47.806 for j in range(blo, bhi):
2025-07-02 05:48:47.814 bj = b[j]
2025-07-02 05:48:47.823 cruncher.set_seq2(bj)
2025-07-02 05:48:47.832 for i in range(alo, ahi):
2025-07-02 05:48:47.839 ai = a[i]
2025-07-02 05:48:47.846 if ai == bj:
2025-07-02 05:48:47.857 if eqi is None:
2025-07-02 05:48:47.870 eqi, eqj = i, j
2025-07-02 05:48:47.879 continue
2025-07-02 05:48:47.887 cruncher.set_seq1(ai)
2025-07-02 05:48:47.894 # computing similarity is expensive, so use the quick
2025-07-02 05:48:47.906 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:47.918 # compares by a factor of 3.
2025-07-02 05:48:47.932 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:47.943 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:47.950 # of the computation is cached by cruncher
2025-07-02 05:48:47.956 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:47.963 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:47.973 cruncher.ratio() > best_ratio:
2025-07-02 05:48:47.982 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:47.991 if best_ratio < cutoff:
2025-07-02 05:48:48.003 # no non-identical "pretty close" pair
2025-07-02 05:48:48.010 if eqi is None:
2025-07-02 05:48:48.016 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:48.022 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:48.028 return
2025-07-02 05:48:48.035 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:48.041 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:48.047 else:
2025-07-02 05:48:48.053 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:48.059 eqi = None
2025-07-02 05:48:48.065
2025-07-02 05:48:48.077 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:48.089 # identical
2025-07-02 05:48:48.102
2025-07-02 05:48:48.112 # pump out diffs from before the synch point
2025-07-02 05:48:48.122 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:48.132
2025-07-02 05:48:48.140 # do intraline marking on the synch pair
2025-07-02 05:48:48.149 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:48.158 if eqi is None:
2025-07-02 05:48:48.169 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:48.176 atags = btags = ""
2025-07-02 05:48:48.182 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:48.188 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:48.193 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:48.198 if tag == 'replace':
2025-07-02 05:48:48.204 atags += '^' * la
2025-07-02 05:48:48.210 btags += '^' * lb
2025-07-02 05:48:48.216 elif tag == 'delete':
2025-07-02 05:48:48.223 atags += '-' * la
2025-07-02 05:48:48.231 elif tag == 'insert':
2025-07-02 05:48:48.238 btags += '+' * lb
2025-07-02 05:48:48.244 elif tag == 'equal':
2025-07-02 05:48:48.250 atags += ' ' * la
2025-07-02 05:48:48.256 btags += ' ' * lb
2025-07-02 05:48:48.262 else:
2025-07-02 05:48:48.269 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:48.282 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:48.293 else:
2025-07-02 05:48:48.303 # the synch pair is identical
2025-07-02 05:48:48.315 yield '  ' + aelt
2025-07-02 05:48:48.325
2025-07-02 05:48:48.335 # pump out diffs from after the synch point
2025-07-02 05:48:48.347 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:48.355
2025-07-02 05:48:48.362 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:48.371 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:48.384
2025-07-02 05:48:48.395 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:48.408 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:48.417 alo = 214, ahi = 1101
2025-07-02 05:48:48.427 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:48.435 blo = 214, bhi = 1101
2025-07-02 05:48:48.442
2025-07-02 05:48:48.449 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:48.457 g = []
2025-07-02 05:48:48.463 if alo < ahi:
2025-07-02 05:48:48.471 if blo < bhi:
2025-07-02 05:48:48.478 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:48.484 else:
2025-07-02 05:48:48.490 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:48.502 elif blo < bhi:
2025-07-02 05:48:48.512 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:48.523
2025-07-02 05:48:48.536 >       yield from g
2025-07-02 05:48:48.548
2025-07-02 05:48:48.558 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:48.570 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:48.580
2025-07-02 05:48:48.589 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:48.596 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:48.603 alo = 214, ahi = 1101
2025-07-02 05:48:48.617 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:48.623 blo = 214, bhi = 1101
2025-07-02 05:48:48.629
2025-07-02 05:48:48.634 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:48.640 r"""
2025-07-02 05:48:48.646 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:48.652 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:48.656 synch point, and intraline difference marking is done on the
2025-07-02 05:48:48.661 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:48.666
2025-07-02 05:48:48.678 Example:
2025-07-02 05:48:48.690
2025-07-02 05:48:48.700 >>> d = Differ()
2025-07-02 05:48:48.710 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:48.722 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:48.732 >>> print(''.join(results), end="")
2025-07-02 05:48:48.740 - abcDefghiJkl
2025-07-02 05:48:48.751 + abcdefGhijkl
2025-07-02 05:48:48.760 """
2025-07-02 05:48:48.764
2025-07-02 05:48:48.771 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:48.782 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:48.793 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:48.801 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:48.808 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:48.814
2025-07-02 05:48:48.820 # search for the pair that matches best without being identical
2025-07-02 05:48:48.825 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:48.829 # on junk -- unless we have to)
2025-07-02 05:48:48.834 for j in range(blo, bhi):
2025-07-02 05:48:48.839 bj = b[j]
2025-07-02 05:48:48.844 cruncher.set_seq2(bj)
2025-07-02 05:48:48.849 for i in range(alo, ahi):
2025-07-02 05:48:48.854 ai = a[i]
2025-07-02 05:48:48.860 if ai == bj:
2025-07-02 05:48:48.866 if eqi is None:
2025-07-02 05:48:48.877 eqi, eqj = i, j
2025-07-02 05:48:48.886 continue
2025-07-02 05:48:48.894 cruncher.set_seq1(ai)
2025-07-02 05:48:48.902 # computing similarity is expensive, so use the quick
2025-07-02 05:48:48.912 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:48.920 # compares by a factor of 3.
2025-07-02 05:48:48.925 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:48.930 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:48.936 # of the computation is cached by cruncher
2025-07-02 05:48:48.941 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:48.947 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:48.952 cruncher.ratio() > best_ratio:
2025-07-02 05:48:48.957 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:48.964 if best_ratio < cutoff:
2025-07-02 05:48:48.970 # no non-identical "pretty close" pair
2025-07-02 05:48:48.976 if eqi is None:
2025-07-02 05:48:48.983 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:48.990 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:49.002 return
2025-07-02 05:48:49.012 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:49.025 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:49.038 else:
2025-07-02 05:48:49.048 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:49.057 eqi = None
2025-07-02 05:48:49.065
2025-07-02 05:48:49.078 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:49.087 # identical
2025-07-02 05:48:49.098
2025-07-02 05:48:49.110 # pump out diffs from before the synch point
2025-07-02 05:48:49.121 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:49.133
2025-07-02 05:48:49.143 # do intraline marking on the synch pair
2025-07-02 05:48:49.155 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:49.166 if eqi is None:
2025-07-02 05:48:49.174 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:49.184 atags = btags = ""
2025-07-02 05:48:49.193 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:49.204 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:49.213 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:49.226 if tag == 'replace':
2025-07-02 05:48:49.236 atags += '^' * la
2025-07-02 05:48:49.244 btags += '^' * lb
2025-07-02 05:48:49.252 elif tag == 'delete':
2025-07-02 05:48:49.258 atags += '-' * la
2025-07-02 05:48:49.264 elif tag == 'insert':
2025-07-02 05:48:49.270 btags += '+' * lb
2025-07-02 05:48:49.280 elif tag == 'equal':
2025-07-02 05:48:49.290 atags += ' ' * la
2025-07-02 05:48:49.298 btags += ' ' * lb
2025-07-02 05:48:49.305 else:
2025-07-02 05:48:49.311 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:49.317 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:49.322 else:
2025-07-02 05:48:49.328 # the synch pair is identical
2025-07-02 05:48:49.334 yield '  ' + aelt
2025-07-02 05:48:49.345
2025-07-02 05:48:49.354 # pump out diffs from after the synch point
2025-07-02 05:48:49.361 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:49.367
2025-07-02 05:48:49.375 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:49.386 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:49.395
2025-07-02 05:48:49.403 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:49.411 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:49.418 alo = 215, ahi = 1101
2025-07-02 05:48:49.428 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:49.437 blo = 215, bhi = 1101
2025-07-02 05:48:49.445
2025-07-02 05:48:49.456 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:49.466 g = []
2025-07-02 05:48:49.475 if alo < ahi:
2025-07-02 05:48:49.485 if blo < bhi:
2025-07-02 05:48:49.492 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:49.499 else:
2025-07-02 05:48:49.506 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:49.512 elif blo < bhi:
2025-07-02 05:48:49.518 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:49.524
2025-07-02 05:48:49.531 >       yield from g
2025-07-02 05:48:49.536
2025-07-02 05:48:49.542 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:49.549 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:49.556
2025-07-02 05:48:49.564 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:49.577 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:49.586 alo = 215, ahi = 1101
2025-07-02 05:48:49.594 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:49.603 blo = 215, bhi = 1101
2025-07-02 05:48:49.612
2025-07-02 05:48:49.619 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:49.626 r"""
2025-07-02 05:48:49.636 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:49.645 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:49.652 synch point, and intraline difference marking is done on the
2025-07-02 05:48:49.659 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:49.669
2025-07-02 05:48:49.677 Example:
2025-07-02 05:48:49.685
2025-07-02 05:48:49.692 >>> d = Differ()
2025-07-02 05:48:49.700 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:49.707 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:49.717 >>> print(''.join(results), end="")
2025-07-02 05:48:49.731 - abcDefghiJkl
2025-07-02 05:48:49.754 + abcdefGhijkl
2025-07-02 05:48:49.776 """
2025-07-02 05:48:49.784
2025-07-02 05:48:49.793 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:49.805 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:49.815 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:49.823 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:49.833 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:49.839
2025-07-02 05:48:49.846 # search for the pair that matches best without being identical
2025-07-02 05:48:49.852 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:49.858 # on junk -- unless we have to)
2025-07-02 05:48:49.866 for j in range(blo, bhi):
2025-07-02 05:48:49.876 bj = b[j]
2025-07-02 05:48:49.886 cruncher.set_seq2(bj)
2025-07-02 05:48:49.895 for i in range(alo, ahi):
2025-07-02 05:48:49.905 ai = a[i]
2025-07-02 05:48:49.915 if ai == bj:
2025-07-02 05:48:49.923 if eqi is None:
2025-07-02 05:48:49.930 eqi, eqj = i, j
2025-07-02 05:48:49.936 continue
2025-07-02 05:48:49.942 cruncher.set_seq1(ai)
2025-07-02 05:48:49.955 # computing similarity is expensive, so use the quick
2025-07-02 05:48:49.966 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:49.975 # compares by a factor of 3.
2025-07-02 05:48:49.982 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:49.992 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:50.005 # of the computation is cached by cruncher
2025-07-02 05:48:50.018 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:50.027 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:50.035 cruncher.ratio() > best_ratio:
2025-07-02 05:48:50.042 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:50.051 if best_ratio < cutoff:
2025-07-02 05:48:50.058 # no non-identical "pretty close" pair
2025-07-02 05:48:50.066 if eqi is None:
2025-07-02 05:48:50.077 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:50.086 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:50.093 return
2025-07-02 05:48:50.106 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:50.116 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:50.123 else:
2025-07-02 05:48:50.132 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:50.141 eqi = None
2025-07-02 05:48:50.148
2025-07-02 05:48:50.161 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:50.172 # identical
2025-07-02 05:48:50.182
2025-07-02 05:48:50.195 # pump out diffs from before the synch point
2025-07-02 05:48:50.207 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:50.217
2025-07-02 05:48:50.224 # do intraline marking on the synch pair
2025-07-02 05:48:50.230 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:50.235 if eqi is None:
2025-07-02 05:48:50.249 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:50.259 atags = btags = ""
2025-07-02 05:48:50.270 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:50.279 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:50.287 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:50.294 if tag == 'replace':
2025-07-02 05:48:50.304 atags += '^' * la
2025-07-02 05:48:50.313 btags += '^' * lb
2025-07-02 05:48:50.320 elif tag == 'delete':
2025-07-02 05:48:50.327 atags += '-' * la
2025-07-02 05:48:50.332 elif tag == 'insert':
2025-07-02 05:48:50.338 btags += '+' * lb
2025-07-02 05:48:50.344 elif tag == 'equal':
2025-07-02 05:48:50.356 atags += ' ' * la
2025-07-02 05:48:50.369 btags += ' ' * lb
2025-07-02 05:48:50.381 else:
2025-07-02 05:48:50.393 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:50.405 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:50.415 else:
2025-07-02 05:48:50.424 # the synch pair is identical
2025-07-02 05:48:50.432 yield '  ' + aelt
2025-07-02 05:48:50.438
2025-07-02 05:48:50.449 # pump out diffs from after the synch point
2025-07-02 05:48:50.459 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:50.467
2025-07-02 05:48:50.476 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:50.483 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:50.490
2025-07-02 05:48:50.503 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:50.515 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:50.523 alo = 216, ahi = 1101
2025-07-02 05:48:50.532 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:50.538 blo = 216, bhi = 1101
2025-07-02 05:48:50.551
2025-07-02 05:48:50.561 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:50.569 g = []
2025-07-02 05:48:50.575 if alo < ahi:
2025-07-02 05:48:50.581 if blo < bhi:
2025-07-02 05:48:50.586 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:50.590 else:
2025-07-02 05:48:50.596 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:50.601 elif blo < bhi:
2025-07-02 05:48:50.607 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:50.616
2025-07-02 05:48:50.627 >       yield from g
2025-07-02 05:48:50.636
2025-07-02 05:48:50.643 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:50.650 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:50.656
2025-07-02 05:48:50.666 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:50.675 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:50.686 alo = 216, ahi = 1101
2025-07-02 05:48:50.697 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:50.704 blo = 216, bhi = 1101
2025-07-02 05:48:50.710
2025-07-02 05:48:50.719 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:50.727 r"""
2025-07-02 05:48:50.734 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:50.742 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:50.750 synch point, and intraline difference marking is done on the
2025-07-02 05:48:50.759 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:50.770
2025-07-02 05:48:50.783 Example:
2025-07-02 05:48:50.795
2025-07-02 05:48:50.805 >>> d = Differ()
2025-07-02 05:48:50.815 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:50.826 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:50.835 >>> print(''.join(results), end="")
2025-07-02 05:48:50.842 - abcDefghiJkl
2025-07-02 05:48:50.863 + abcdefGhijkl
2025-07-02 05:48:50.878 """
2025-07-02 05:48:50.884
2025-07-02 05:48:50.891 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:50.899 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:50.909 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:50.918 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:50.928 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:50.937
2025-07-02 05:48:50.948 # search for the pair that matches best without being identical
2025-07-02 05:48:50.960 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:50.968 # on junk -- unless we have to)
2025-07-02 05:48:50.974 for j in range(blo, bhi):
2025-07-02 05:48:50.980 bj = b[j]
2025-07-02 05:48:50.986 cruncher.set_seq2(bj)
2025-07-02 05:48:50.994 for i in range(alo, ahi):
2025-07-02 05:48:51.000 ai = a[i]
2025-07-02 05:48:51.004 if ai == bj:
2025-07-02 05:48:51.010 if eqi is None:
2025-07-02 05:48:51.015 eqi, eqj = i, j
2025-07-02 05:48:51.023 continue
2025-07-02 05:48:51.031 cruncher.set_seq1(ai)
2025-07-02 05:48:51.041 # computing similarity is expensive, so use the quick
2025-07-02 05:48:51.049 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:51.055 # compares by a factor of 3.
2025-07-02 05:48:51.061 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:51.067 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:51.073 # of the computation is cached by cruncher
2025-07-02 05:48:51.079 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:51.085 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:51.091 cruncher.ratio() > best_ratio:
2025-07-02 05:48:51.099 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:51.110 if best_ratio < cutoff:
2025-07-02 05:48:51.120 # no non-identical "pretty close" pair
2025-07-02 05:48:51.127 if eqi is None:
2025-07-02 05:48:51.134 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:51.141 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:51.146 return
2025-07-02 05:48:51.152 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:51.159 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:51.166 else:
2025-07-02 05:48:51.172 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:51.177 eqi = None
2025-07-02 05:48:51.183
2025-07-02 05:48:51.191 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:51.204 # identical
2025-07-02 05:48:51.214
2025-07-02 05:48:51.227 # pump out diffs from before the synch point
2025-07-02 05:48:51.238 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:51.250
2025-07-02 05:48:51.262 # do intraline marking on the synch pair
2025-07-02 05:48:51.273 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:51.283 if eqi is None:
2025-07-02 05:48:51.295 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:51.307 atags = btags = ""
2025-07-02 05:48:51.320 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:51.331 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:51.342 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:51.351 if tag == 'replace':
2025-07-02 05:48:51.359 atags += '^' * la
2025-07-02 05:48:51.367 btags += '^' * lb
2025-07-02 05:48:51.380 elif tag == 'delete':
2025-07-02 05:48:51.389 atags += '-' * la
2025-07-02 05:48:51.396 elif tag == 'insert':
2025-07-02 05:48:51.402 btags += '+' * lb
2025-07-02 05:48:51.409 elif tag == 'equal':
2025-07-02 05:48:51.414 atags += ' ' * la
2025-07-02 05:48:51.420 btags += ' ' * lb
2025-07-02 05:48:51.426 else:
2025-07-02 05:48:51.436 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:51.447 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:51.459 else:
2025-07-02 05:48:51.471 # the synch pair is identical
2025-07-02 05:48:51.480 yield '  ' + aelt
2025-07-02 05:48:51.488
2025-07-02 05:48:51.500 # pump out diffs from after the synch point
2025-07-02 05:48:51.509 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:51.516
2025-07-02 05:48:51.523 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:51.533 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:51.545
2025-07-02 05:48:51.556 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:51.566 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:51.573 alo = 217, ahi = 1101
2025-07-02 05:48:51.589 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:51.599 blo = 217, bhi = 1101
2025-07-02 05:48:51.606
2025-07-02 05:48:51.613 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:51.620 g = []
2025-07-02 05:48:51.625 if alo < ahi:
2025-07-02 05:48:51.631 if blo < bhi:
2025-07-02 05:48:51.636 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:51.641 else:
2025-07-02 05:48:51.647 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:51.653 elif blo < bhi:
2025-07-02 05:48:51.659 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:51.667
2025-07-02 05:48:51.676 >       yield from g
2025-07-02 05:48:51.686
2025-07-02 05:48:51.694 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:51.700 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:51.706
2025-07-02 05:48:51.712 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:51.721 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:51.727 alo = 217, ahi = 1101
2025-07-02 05:48:51.733 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:51.738 blo = 217, bhi = 1101
2025-07-02 05:48:51.743
2025-07-02 05:48:51.749 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:51.755 r"""
2025-07-02 05:48:51.762 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:51.774 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:51.783 synch point, and intraline difference marking is done on the
2025-07-02 05:48:51.790 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:51.798
2025-07-02 05:48:51.807 Example:
2025-07-02 05:48:51.815
2025-07-02 05:48:51.822 >>> d = Differ()
2025-07-02 05:48:51.831 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:51.838 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:51.844 >>> print(''.join(results), end="")
2025-07-02 05:48:51.851 - abcDefghiJkl
2025-07-02 05:48:51.871 + abcdefGhijkl
2025-07-02 05:48:51.888 """
2025-07-02 05:48:51.897
2025-07-02 05:48:51.905 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:51.911 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:51.919 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:51.928 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:51.940 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:51.948
2025-07-02 05:48:51.956 # search for the pair that matches best without being identical
2025-07-02 05:48:51.963 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:51.969 # on junk -- unless we have to)
2025-07-02 05:48:51.981 for j in range(blo, bhi):
2025-07-02 05:48:51.992 bj = b[j]
2025-07-02 05:48:52.000 cruncher.set_seq2(bj)
2025-07-02 05:48:52.007 for i in range(alo, ahi):
2025-07-02 05:48:52.014 ai = a[i]
2025-07-02 05:48:52.021 if ai == bj:
2025-07-02 05:48:52.026 if eqi is None:
2025-07-02 05:48:52.033 eqi, eqj = i, j
2025-07-02 05:48:52.044 continue
2025-07-02 05:48:52.052 cruncher.set_seq1(ai)
2025-07-02 05:48:52.066 # computing similarity is expensive, so use the quick
2025-07-02 05:48:52.076 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:52.087 # compares by a factor of 3.
2025-07-02 05:48:52.099 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:52.109 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:52.119 # of the computation is cached by cruncher
2025-07-02 05:48:52.131 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:52.139 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:52.147 cruncher.ratio() > best_ratio:
2025-07-02 05:48:52.155 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:52.166 if best_ratio < cutoff:
2025-07-02 05:48:52.175 # no non-identical "pretty close" pair
2025-07-02 05:48:52.183 if eqi is None:
2025-07-02 05:48:52.190 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:52.197 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:52.203 return
2025-07-02 05:48:52.211 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:52.221 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:52.229 else:
2025-07-02 05:48:52.236 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:52.243 eqi = None
2025-07-02 05:48:52.252
2025-07-02 05:48:52.260 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:52.267 # identical
2025-07-02 05:48:52.274
2025-07-02 05:48:52.284 # pump out diffs from before the synch point
2025-07-02 05:48:52.293 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:52.302
2025-07-02 05:48:52.308 # do intraline marking on the synch pair
2025-07-02 05:48:52.314 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:52.319 if eqi is None:
2025-07-02 05:48:52.325 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:52.330 atags = btags = ""
2025-07-02 05:48:52.341 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:52.350 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:52.358 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:52.366 if tag == 'replace':
2025-07-02 05:48:52.375 atags += '^' * la
2025-07-02 05:48:52.385 btags += '^' * lb
2025-07-02 05:48:52.397 elif tag == 'delete':
2025-07-02 05:48:52.409 atags += '-' * la
2025-07-02 05:48:52.420 elif tag == 'insert':
2025-07-02 05:48:52.427 btags += '+' * lb
2025-07-02 05:48:52.435 elif tag == 'equal':
2025-07-02 05:48:52.441 atags += ' ' * la
2025-07-02 05:48:52.447 btags += ' ' * lb
2025-07-02 05:48:52.455 else:
2025-07-02 05:48:52.467 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:52.475 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:52.482 else:
2025-07-02 05:48:52.489 # the synch pair is identical
2025-07-02 05:48:52.495 yield '  ' + aelt
2025-07-02 05:48:52.500
2025-07-02 05:48:52.512 # pump out diffs from after the synch point
2025-07-02 05:48:52.522 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:52.531
2025-07-02 05:48:52.539 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:52.551 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:52.560
2025-07-02 05:48:52.568 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:52.581 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:52.590 alo = 218, ahi = 1101
2025-07-02 05:48:52.598 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:52.604 blo = 218, bhi = 1101
2025-07-02 05:48:52.611
2025-07-02 05:48:52.623 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:52.637 g = []
2025-07-02 05:48:52.649 if alo < ahi:
2025-07-02 05:48:52.663 if blo < bhi:
2025-07-02 05:48:52.673 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:52.684 else:
2025-07-02 05:48:52.694 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:52.702 elif blo < bhi:
2025-07-02 05:48:52.709 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:52.715
2025-07-02 05:48:52.724 >       yield from g
2025-07-02 05:48:52.736
2025-07-02 05:48:52.745 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:52.758 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:52.772
2025-07-02 05:48:52.782 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:52.795 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:52.803 alo = 218, ahi = 1101
2025-07-02 05:48:52.812 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:52.818 blo = 218, bhi = 1101
2025-07-02 05:48:52.824
2025-07-02 05:48:52.830 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:52.838 r"""
2025-07-02 05:48:52.845 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:52.851 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:52.858 synch point, and intraline difference marking is done on the
2025-07-02 05:48:52.864 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:52.873
2025-07-02 05:48:52.886 Example:
2025-07-02 05:48:52.896
2025-07-02 05:48:52.907 >>> d = Differ()
2025-07-02 05:48:52.919 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:52.929 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:52.941 >>> print(''.join(results), end="")
2025-07-02 05:48:52.950 - abcDefghiJkl
2025-07-02 05:48:52.968 + abcdefGhijkl
2025-07-02 05:48:52.991 """
2025-07-02 05:48:53.000
2025-07-02 05:48:53.009 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:53.021 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:53.032 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:53.043 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:53.051 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:53.059
2025-07-02 05:48:53.067 # search for the pair that matches best without being identical
2025-07-02 05:48:53.075 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:53.082 # on junk -- unless we have to)
2025-07-02 05:48:53.090 for j in range(blo, bhi):
2025-07-02 05:48:53.101 bj = b[j]
2025-07-02 05:48:53.112 cruncher.set_seq2(bj)
2025-07-02 05:48:53.123 for i in range(alo, ahi):
2025-07-02 05:48:53.130 ai = a[i]
2025-07-02 05:48:53.142 if ai == bj:
2025-07-02 05:48:53.154 if eqi is None:
2025-07-02 05:48:53.166 eqi, eqj = i, j
2025-07-02 05:48:53.176 continue
2025-07-02 05:48:53.184 cruncher.set_seq1(ai)
2025-07-02 05:48:53.190 # computing similarity is expensive, so use the quick
2025-07-02 05:48:53.196 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:53.201 # compares by a factor of 3.
2025-07-02 05:48:53.206 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:53.213 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:53.221 # of the computation is cached by cruncher
2025-07-02 05:48:53.227 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:53.234 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:53.241 cruncher.ratio() > best_ratio:
2025-07-02 05:48:53.247 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:53.252 if best_ratio < cutoff:
2025-07-02 05:48:53.271 # no non-identical "pretty close" pair
2025-07-02 05:48:53.280 if eqi is None:
2025-07-02 05:48:53.287 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:53.295 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:53.301 return
2025-07-02 05:48:53.307 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:53.313 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:53.319 else:
2025-07-02 05:48:53.327 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:53.336 eqi = None
2025-07-02 05:48:53.344
2025-07-02 05:48:53.351 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:53.357 # identical
2025-07-02 05:48:53.362
2025-07-02 05:48:53.368 # pump out diffs from before the synch point
2025-07-02 05:48:53.373 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:53.377
2025-07-02 05:48:53.382 # do intraline marking on the synch pair
2025-07-02 05:48:53.387 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:53.391 if eqi is None:
2025-07-02 05:48:53.396 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:53.401 atags = btags = ""
2025-07-02 05:48:53.406 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:53.412 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:53.421 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:53.432 if tag == 'replace':
2025-07-02 05:48:53.440 atags += '^' * la
2025-07-02 05:48:53.448 btags += '^' * lb
2025-07-02 05:48:53.455 elif tag == 'delete':
2025-07-02 05:48:53.461 atags += '-' * la
2025-07-02 05:48:53.467 elif tag == 'insert':
2025-07-02 05:48:53.473 btags += '+' * lb
2025-07-02 05:48:53.479 elif tag == 'equal':
2025-07-02 05:48:53.485 atags += ' ' * la
2025-07-02 05:48:53.491 btags += ' ' * lb
2025-07-02 05:48:53.496 else:
2025-07-02 05:48:53.503 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:53.511 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:53.518 else:
2025-07-02 05:48:53.530 # the synch pair is identical
2025-07-02 05:48:53.540 yield '  ' + aelt
2025-07-02 05:48:53.547
2025-07-02 05:48:53.555 # pump out diffs from after the synch point
2025-07-02 05:48:53.566 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:53.576
2025-07-02 05:48:53.585 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:53.592 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:53.598
2025-07-02 05:48:53.607 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:53.613 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:53.620 alo = 219, ahi = 1101
2025-07-02 05:48:53.628 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:53.635 blo = 219, bhi = 1101
2025-07-02 05:48:53.642
2025-07-02 05:48:53.650 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:53.661 g = []
2025-07-02 05:48:53.671 if alo < ahi:
2025-07-02 05:48:53.680 if blo < bhi:
2025-07-02 05:48:53.687 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:53.693 else:
2025-07-02 05:48:53.699 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:53.707 elif blo < bhi:
2025-07-02 05:48:53.714 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:53.726
2025-07-02 05:48:53.737 >       yield from g
2025-07-02 05:48:53.746
2025-07-02 05:48:53.752 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:53.759 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:53.765
2025-07-02 05:48:53.776 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:53.786 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:53.793 alo = 219, ahi = 1101
2025-07-02 05:48:53.800 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:53.807 blo = 219, bhi = 1101
2025-07-02 05:48:53.814
2025-07-02 05:48:53.825 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:53.835 r"""
2025-07-02 05:48:53.842 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:53.848 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:53.855 synch point, and intraline difference marking is done on the
2025-07-02 05:48:53.863 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:53.871
2025-07-02 05:48:53.880 Example:
2025-07-02 05:48:53.888
2025-07-02 05:48:53.895 >>> d = Differ()
2025-07-02 05:48:53.903 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:53.911 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:53.918 >>> print(''.join(results), end="")
2025-07-02 05:48:53.926 - abcDefghiJkl
2025-07-02 05:48:53.948 + abcdefGhijkl
2025-07-02 05:48:53.962 """
2025-07-02 05:48:53.967
2025-07-02 05:48:53.972 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:53.978 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:53.983 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:53.990 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:53.996 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:54.003
2025-07-02 05:48:54.011 # search for the pair that matches best without being identical
2025-07-02 05:48:54.018 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:54.027 # on junk -- unless we have to)
2025-07-02 05:48:54.040 for j in range(blo, bhi):
2025-07-02 05:48:54.048 bj = b[j]
2025-07-02 05:48:54.054 cruncher.set_seq2(bj)
2025-07-02 05:48:54.060 for i in range(alo, ahi):
2025-07-02 05:48:54.065 ai = a[i]
2025-07-02 05:48:54.077 if ai == bj:
2025-07-02 05:48:54.088 if eqi is None:
2025-07-02 05:48:54.097 eqi, eqj = i, j
2025-07-02 05:48:54.107 continue
2025-07-02 05:48:54.120 cruncher.set_seq1(ai)
2025-07-02 05:48:54.129 # computing similarity is expensive, so use the quick
2025-07-02 05:48:54.140 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:54.150 # compares by a factor of 3.
2025-07-02 05:48:54.160 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:54.169 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:54.176 # of the computation is cached by cruncher
2025-07-02 05:48:54.186 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:54.199 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:54.208 cruncher.ratio() > best_ratio:
2025-07-02 05:48:54.220 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:54.229 if best_ratio < cutoff:
2025-07-02 05:48:54.238 # no non-identical "pretty close" pair
2025-07-02 05:48:54.254 if eqi is None:
2025-07-02 05:48:54.265 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:54.274 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:54.283 return
2025-07-02 05:48:54.294 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:54.304 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:54.316 else:
2025-07-02 05:48:54.326 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:54.339 eqi = None
2025-07-02 05:48:54.351
2025-07-02 05:48:54.364 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:54.375 # identical
2025-07-02 05:48:54.385
2025-07-02 05:48:54.393 # pump out diffs from before the synch point
2025-07-02 05:48:54.405 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:54.416
2025-07-02 05:48:54.426 # do intraline marking on the synch pair
2025-07-02 05:48:54.433 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:54.440 if eqi is None:
2025-07-02 05:48:54.446 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:54.450 atags = btags = ""
2025-07-02 05:48:54.456 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:54.462 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:54.467 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:54.472 if tag == 'replace':
2025-07-02 05:48:54.480 atags += '^' * la
2025-07-02 05:48:54.489 btags += '^' * lb
2025-07-02 05:48:54.497 elif tag == 'delete':
2025-07-02 05:48:54.503 atags += '-' * la
2025-07-02 05:48:54.512 elif tag == 'insert':
2025-07-02 05:48:54.520 btags += '+' * lb
2025-07-02 05:48:54.527 elif tag == 'equal':
2025-07-02 05:48:54.538 atags += ' ' * la
2025-07-02 05:48:54.551 btags += ' ' * lb
2025-07-02 05:48:54.561 else:
2025-07-02 05:48:54.572 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:54.584 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:54.596 else:
2025-07-02 05:48:54.607 # the synch pair is identical
2025-07-02 05:48:54.621 yield '  ' + aelt
2025-07-02 05:48:54.628
2025-07-02 05:48:54.635 # pump out diffs from after the synch point
2025-07-02 05:48:54.642 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:54.648
2025-07-02 05:48:54.655 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:54.663 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:54.673
2025-07-02 05:48:54.681 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:54.691 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:54.700 alo = 220, ahi = 1101
2025-07-02 05:48:54.714 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:54.725 blo = 220, bhi = 1101
2025-07-02 05:48:54.738
2025-07-02 05:48:54.750 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:54.763 g = []
2025-07-02 05:48:54.774 if alo < ahi:
2025-07-02 05:48:54.787 if blo < bhi:
2025-07-02 05:48:54.797 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:54.806 else:
2025-07-02 05:48:54.815 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:54.827 elif blo < bhi:
2025-07-02 05:48:54.836 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:54.849
2025-07-02 05:48:54.861 >       yield from g
2025-07-02 05:48:54.870
2025-07-02 05:48:54.878 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:54.889 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:54.898
2025-07-02 05:48:54.906 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:54.913 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:54.924 alo = 220, ahi = 1101
2025-07-02 05:48:54.933 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:54.939 blo = 220, bhi = 1101
2025-07-02 05:48:54.944
2025-07-02 05:48:54.951 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:54.959 r"""
2025-07-02 05:48:54.970 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:54.981 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:54.991 synch point, and intraline difference marking is done on the
2025-07-02 05:48:55.003 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:55.013
2025-07-02 05:48:55.021 Example:
2025-07-02 05:48:55.027
2025-07-02 05:48:55.034 >>> d = Differ()
2025-07-02 05:48:55.040 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:55.046 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:55.053 >>> print(''.join(results), end="")
2025-07-02 05:48:55.061 - abcDefghiJkl
2025-07-02 05:48:55.076 + abcdefGhijkl
2025-07-02 05:48:55.091 """
2025-07-02 05:48:55.098
2025-07-02 05:48:55.106 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:55.115 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:55.128 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:55.137 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:55.144 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:55.150
2025-07-02 05:48:55.157 # search for the pair that matches best without being identical
2025-07-02 05:48:55.163 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:55.170 # on junk -- unless we have to)
2025-07-02 05:48:55.177 for j in range(blo, bhi):
2025-07-02 05:48:55.184 bj = b[j]
2025-07-02 05:48:55.190 cruncher.set_seq2(bj)
2025-07-02 05:48:55.196 for i in range(alo, ahi):
2025-07-02 05:48:55.202 ai = a[i]
2025-07-02 05:48:55.209 if ai == bj:
2025-07-02 05:48:55.215 if eqi is None:
2025-07-02 05:48:55.223 eqi, eqj = i, j
2025-07-02 05:48:55.234 continue
2025-07-02 05:48:55.242 cruncher.set_seq1(ai)
2025-07-02 05:48:55.250 # computing similarity is expensive, so use the quick
2025-07-02 05:48:55.259 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:55.270 # compares by a factor of 3.
2025-07-02 05:48:55.279 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:55.286 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:55.293 # of the computation is cached by cruncher
2025-07-02 05:48:55.298 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:55.303 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:55.310 cruncher.ratio() > best_ratio:
2025-07-02 05:48:55.315 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:55.321 if best_ratio < cutoff:
2025-07-02 05:48:55.326 # no non-identical "pretty close" pair
2025-07-02 05:48:55.333 if eqi is None:
2025-07-02 05:48:55.344 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:55.354 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:55.365 return
2025-07-02 05:48:55.379 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:55.392 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:55.405 else:
2025-07-02 05:48:55.420 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:55.431 eqi = None
2025-07-02 05:48:55.439
2025-07-02 05:48:55.447 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:55.455 # identical
2025-07-02 05:48:55.467
2025-07-02 05:48:55.476 # pump out diffs from before the synch point
2025-07-02 05:48:55.485 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:55.492
2025-07-02 05:48:55.499 # do intraline marking on the synch pair
2025-07-02 05:48:55.504 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:55.510 if eqi is None:
2025-07-02 05:48:55.521 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:55.532 atags = btags = ""
2025-07-02 05:48:55.540 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:55.546 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:55.552 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:55.558 if tag == 'replace':
2025-07-02 05:48:55.569 atags += '^' * la
2025-07-02 05:48:55.582 btags += '^' * lb
2025-07-02 05:48:55.591 elif tag == 'delete':
2025-07-02 05:48:55.600 atags += '-' * la
2025-07-02 05:48:55.607 elif tag == 'insert':
2025-07-02 05:48:55.613 btags += '+' * lb
2025-07-02 05:48:55.619 elif tag == 'equal':
2025-07-02 05:48:55.624 atags += ' ' * la
2025-07-02 05:48:55.631 btags += ' ' * lb
2025-07-02 05:48:55.638 else:
2025-07-02 05:48:55.649 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:55.657 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:55.663 else:
2025-07-02 05:48:55.677 # the synch pair is identical
2025-07-02 05:48:55.688 yield '  ' + aelt
2025-07-02 05:48:55.698
2025-07-02 05:48:55.707 # pump out diffs from after the synch point
2025-07-02 05:48:55.715 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:55.722
2025-07-02 05:48:55.729 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:55.735 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:55.741
2025-07-02 05:48:55.746 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:55.754 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:55.764 alo = 221, ahi = 1101
2025-07-02 05:48:55.774 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:55.786 blo = 221, bhi = 1101
2025-07-02 05:48:55.799
2025-07-02 05:48:55.811 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:55.821 g = []
2025-07-02 05:48:55.836 if alo < ahi:
2025-07-02 05:48:55.846 if blo < bhi:
2025-07-02 05:48:55.855 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:55.862 else:
2025-07-02 05:48:55.872 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:55.883 elif blo < bhi:
2025-07-02 05:48:55.891 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:55.904
2025-07-02 05:48:55.915 >       yield from g
2025-07-02 05:48:55.923
2025-07-02 05:48:55.931 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:55.938 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:55.944
2025-07-02 05:48:55.957 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:55.968 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:55.977 alo = 221, ahi = 1101
2025-07-02 05:48:55.987 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:55.999 blo = 221, bhi = 1101
2025-07-02 05:48:56.008
2025-07-02 05:48:56.015 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:56.022 r"""
2025-07-02 05:48:56.034 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:56.043 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:56.051 synch point, and intraline difference marking is done on the
2025-07-02 05:48:56.058 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:56.070
2025-07-02 05:48:56.079 Example:
2025-07-02 05:48:56.090
2025-07-02 05:48:56.103 >>> d = Differ()
2025-07-02 05:48:56.114 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:56.125 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:56.133 >>> print(''.join(results), end="")
2025-07-02 05:48:56.139 - abcDefghiJkl
2025-07-02 05:48:56.167 + abcdefGhijkl
2025-07-02 05:48:56.184 """
2025-07-02 05:48:56.195
2025-07-02 05:48:56.206 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:56.218 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:56.230 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:56.242 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:56.256 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:56.265
2025-07-02 05:48:56.273 # search for the pair that matches best without being identical
2025-07-02 05:48:56.281 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:56.287 # on junk -- unless we have to)
2025-07-02 05:48:56.294 for j in range(blo, bhi):
2025-07-02 05:48:56.300 bj = b[j]
2025-07-02 05:48:56.306 cruncher.set_seq2(bj)
2025-07-02 05:48:56.320 for i in range(alo, ahi):
2025-07-02 05:48:56.332 ai = a[i]
2025-07-02 05:48:56.340 if ai == bj:
2025-07-02 05:48:56.348 if eqi is None:
2025-07-02 05:48:56.355 eqi, eqj = i, j
2025-07-02 05:48:56.362 continue
2025-07-02 05:48:56.368 cruncher.set_seq1(ai)
2025-07-02 05:48:56.375 # computing similarity is expensive, so use the quick
2025-07-02 05:48:56.382 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:56.394 # compares by a factor of 3.
2025-07-02 05:48:56.403 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:56.417 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:56.427 # of the computation is cached by cruncher
2025-07-02 05:48:56.435 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:56.442 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:56.448 cruncher.ratio() > best_ratio:
2025-07-02 05:48:56.454 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:56.463 if best_ratio < cutoff:
2025-07-02 05:48:56.475 # no non-identical "pretty close" pair
2025-07-02 05:48:56.484 if eqi is None:
2025-07-02 05:48:56.493 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:56.499 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:56.506 return
2025-07-02 05:48:56.517 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:56.526 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:56.534 else:
2025-07-02 05:48:56.541 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:56.549 eqi = None
2025-07-02 05:48:56.556
2025-07-02 05:48:56.563 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:56.571 # identical
2025-07-02 05:48:56.579
2025-07-02 05:48:56.589 # pump out diffs from before the synch point
2025-07-02 05:48:56.599 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:56.609
2025-07-02 05:48:56.623 # do intraline marking on the synch pair
2025-07-02 05:48:56.636 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:56.650 if eqi is None:
2025-07-02 05:48:56.664 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:56.675 atags = btags = ""
2025-07-02 05:48:56.685 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:56.695 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:56.703 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:56.710 if tag == 'replace':
2025-07-02 05:48:56.715 atags += '^' * la
2025-07-02 05:48:56.723 btags += '^' * lb
2025-07-02 05:48:56.735 elif tag == 'delete':
2025-07-02 05:48:56.743 atags += '-' * la
2025-07-02 05:48:56.750 elif tag == 'insert':
2025-07-02 05:48:56.756 btags += '+' * lb
2025-07-02 05:48:56.762 elif tag == 'equal':
2025-07-02 05:48:56.774 atags += ' ' * la
2025-07-02 05:48:56.784 btags += ' ' * lb
2025-07-02 05:48:56.792 else:
2025-07-02 05:48:56.798 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:56.809 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:56.819 else:
2025-07-02 05:48:56.826 # the synch pair is identical
2025-07-02 05:48:56.835 yield '  ' + aelt
2025-07-02 05:48:56.848
2025-07-02 05:48:56.860 # pump out diffs from after the synch point
2025-07-02 05:48:56.869 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:56.877
2025-07-02 05:48:56.888 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:56.898 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:56.911
2025-07-02 05:48:56.921 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:56.935 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:56.945 alo = 224, ahi = 1101
2025-07-02 05:48:56.961 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:56.971 blo = 224, bhi = 1101
2025-07-02 05:48:56.983
2025-07-02 05:48:56.994 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:57.006 g = []
2025-07-02 05:48:57.016 if alo < ahi:
2025-07-02 05:48:57.029 if blo < bhi:
2025-07-02 05:48:57.042 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:57.054 else:
2025-07-02 05:48:57.066 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:57.077 elif blo < bhi:
2025-07-02 05:48:57.088 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:57.097
2025-07-02 05:48:57.105 >       yield from g
2025-07-02 05:48:57.112
2025-07-02 05:48:57.119 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:57.127 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:57.138
2025-07-02 05:48:57.145 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:57.151 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:57.160 alo = 224, ahi = 1101
2025-07-02 05:48:57.173 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:57.188 blo = 224, bhi = 1101
2025-07-02 05:48:57.201
2025-07-02 05:48:57.212 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:57.224 r"""
2025-07-02 05:48:57.236 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:57.249 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:57.261 synch point, and intraline difference marking is done on the
2025-07-02 05:48:57.269 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:57.276
2025-07-02 05:48:57.282 Example:
2025-07-02 05:48:57.289
2025-07-02 05:48:57.296 >>> d = Differ()
2025-07-02 05:48:57.303 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:57.312 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:57.323 >>> print(''.join(results), end="")
2025-07-02 05:48:57.332 - abcDefghiJkl
2025-07-02 05:48:57.352 + abcdefGhijkl
2025-07-02 05:48:57.369 """
2025-07-02 05:48:57.375
2025-07-02 05:48:57.383 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:57.398 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:57.406 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:57.415 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:57.422 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:57.428
2025-07-02 05:48:57.434 # search for the pair that matches best without being identical
2025-07-02 05:48:57.439 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:57.444 # on junk -- unless we have to)
2025-07-02 05:48:57.449 for j in range(blo, bhi):
2025-07-02 05:48:57.454 bj = b[j]
2025-07-02 05:48:57.460 cruncher.set_seq2(bj)
2025-07-02 05:48:57.468 for i in range(alo, ahi):
2025-07-02 05:48:57.474 ai = a[i]
2025-07-02 05:48:57.480 if ai == bj:
2025-07-02 05:48:57.486 if eqi is None:
2025-07-02 05:48:57.499 eqi, eqj = i, j
2025-07-02 05:48:57.508 continue
2025-07-02 05:48:57.516 cruncher.set_seq1(ai)
2025-07-02 05:48:57.526 # computing similarity is expensive, so use the quick
2025-07-02 05:48:57.539 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:57.552 # compares by a factor of 3.
2025-07-02 05:48:57.562 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:57.571 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:57.578 # of the computation is cached by cruncher
2025-07-02 05:48:57.585 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:57.592 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:57.598 cruncher.ratio() > best_ratio:
2025-07-02 05:48:57.609 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:57.622 if best_ratio < cutoff:
2025-07-02 05:48:57.631 # no non-identical "pretty close" pair
2025-07-02 05:48:57.644 if eqi is None:
2025-07-02 05:48:57.656 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:57.665 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:57.673 return
2025-07-02 05:48:57.681 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:57.687 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:57.693 else:
2025-07-02 05:48:57.699 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:57.707 eqi = None
2025-07-02 05:48:57.717
2025-07-02 05:48:57.726 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:57.736 # identical
2025-07-02 05:48:57.747
2025-07-02 05:48:57.755 # pump out diffs from before the synch point
2025-07-02 05:48:57.762 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:57.774
2025-07-02 05:48:57.785 # do intraline marking on the synch pair
2025-07-02 05:48:57.794 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:57.805 if eqi is None:
2025-07-02 05:48:57.816 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:57.824 atags = btags = ""
2025-07-02 05:48:57.832 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:57.843 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:57.851 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:57.858 if tag == 'replace':
2025-07-02 05:48:57.870 atags += '^' * la
2025-07-02 05:48:57.880 btags += '^' * lb
2025-07-02 05:48:57.887 elif tag == 'delete':
2025-07-02 05:48:57.895 atags += '-' * la
2025-07-02 05:48:57.905 elif tag == 'insert':
2025-07-02 05:48:57.914 btags += '+' * lb
2025-07-02 05:48:57.921 elif tag == 'equal':
2025-07-02 05:48:57.932 atags += ' ' * la
2025-07-02 05:48:57.941 btags += ' ' * lb
2025-07-02 05:48:57.950 else:
2025-07-02 05:48:57.958 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:57.966 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:57.977 else:
2025-07-02 05:48:57.985 # the synch pair is identical
2025-07-02 05:48:57.992 yield '  ' + aelt
2025-07-02 05:48:57.998
2025-07-02 05:48:58.010 # pump out diffs from after the synch point
2025-07-02 05:48:58.019 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:58.027
2025-07-02 05:48:58.034 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:58.044 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:58.052
2025-07-02 05:48:58.060 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:58.067 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:58.076 alo = 225, ahi = 1101
2025-07-02 05:48:58.089 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:58.101 blo = 225, bhi = 1101
2025-07-02 05:48:58.112
2025-07-02 05:48:58.121 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:58.128 g = []
2025-07-02 05:48:58.135 if alo < ahi:
2025-07-02 05:48:58.143 if blo < bhi:
2025-07-02 05:48:58.155 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:58.164 else:
2025-07-02 05:48:58.170 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:58.177 elif blo < bhi:
2025-07-02 05:48:58.183 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:58.193
2025-07-02 05:48:58.203 >       yield from g
2025-07-02 05:48:58.213
2025-07-02 05:48:58.224 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:58.235 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:58.244
2025-07-02 05:48:58.253 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:58.264 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:58.276 alo = 225, ahi = 1101
2025-07-02 05:48:58.291 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:58.303 blo = 225, bhi = 1101
2025-07-02 05:48:58.312
2025-07-02 05:48:58.320 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:58.327 r"""
2025-07-02 05:48:58.334 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:58.340 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:58.346 synch point, and intraline difference marking is done on the
2025-07-02 05:48:58.351 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:58.359
2025-07-02 05:48:58.370 Example:
2025-07-02 05:48:58.380
2025-07-02 05:48:58.387 >>> d = Differ()
2025-07-02 05:48:58.395 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:58.401 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:58.408 >>> print(''.join(results), end="")
2025-07-02 05:48:58.414 - abcDefghiJkl
2025-07-02 05:48:58.427 + abcdefGhijkl
2025-07-02 05:48:58.447 """
2025-07-02 05:48:58.454
2025-07-02 05:48:58.461 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:58.468 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:58.474 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:58.484 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:58.494 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:58.506
2025-07-02 05:48:58.518 # search for the pair that matches best without being identical
2025-07-02 05:48:58.529 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:58.538 # on junk -- unless we have to)
2025-07-02 05:48:58.548 for j in range(blo, bhi):
2025-07-02 05:48:58.559 bj = b[j]
2025-07-02 05:48:58.567 cruncher.set_seq2(bj)
2025-07-02 05:48:58.574 for i in range(alo, ahi):
2025-07-02 05:48:58.585 ai = a[i]
2025-07-02 05:48:58.596 if ai == bj:
2025-07-02 05:48:58.606 if eqi is None:
2025-07-02 05:48:58.617 eqi, eqj = i, j
2025-07-02 05:48:58.628 continue
2025-07-02 05:48:58.637 cruncher.set_seq1(ai)
2025-07-02 05:48:58.645 # computing similarity is expensive, so use the quick
2025-07-02 05:48:58.652 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:58.658 # compares by a factor of 3.
2025-07-02 05:48:58.669 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:58.677 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:58.689 # of the computation is cached by cruncher
2025-07-02 05:48:58.702 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:58.711 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:58.719 cruncher.ratio() > best_ratio:
2025-07-02 05:48:58.726 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:58.732 if best_ratio < cutoff:
2025-07-02 05:48:58.739 # no non-identical "pretty close" pair
2025-07-02 05:48:58.747 if eqi is None:
2025-07-02 05:48:58.758 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:58.768 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:58.780 return
2025-07-02 05:48:58.792 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:58.805 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:58.815 else:
2025-07-02 05:48:58.823 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:58.831 eqi = None
2025-07-02 05:48:58.839
2025-07-02 05:48:58.851 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:48:58.860 # identical
2025-07-02 05:48:58.867
2025-07-02 05:48:58.880 # pump out diffs from before the synch point
2025-07-02 05:48:58.892 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:48:58.903
2025-07-02 05:48:58.914 # do intraline marking on the synch pair
2025-07-02 05:48:58.926 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:48:58.938 if eqi is None:
2025-07-02 05:48:58.949 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:48:58.959 atags = btags = ""
2025-07-02 05:48:58.967 cruncher.set_seqs(aelt, belt)
2025-07-02 05:48:58.975 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:48:58.987 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:48:58.995 if tag == 'replace':
2025-07-02 05:48:59.002 atags += '^' * la
2025-07-02 05:48:59.009 btags += '^' * lb
2025-07-02 05:48:59.021 elif tag == 'delete':
2025-07-02 05:48:59.032 atags += '-' * la
2025-07-02 05:48:59.041 elif tag == 'insert':
2025-07-02 05:48:59.048 btags += '+' * lb
2025-07-02 05:48:59.055 elif tag == 'equal':
2025-07-02 05:48:59.068 atags += ' ' * la
2025-07-02 05:48:59.083 btags += ' ' * lb
2025-07-02 05:48:59.092 else:
2025-07-02 05:48:59.099 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:48:59.106 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:48:59.112 else:
2025-07-02 05:48:59.118 # the synch pair is identical
2025-07-02 05:48:59.124 yield '  ' + aelt
2025-07-02 05:48:59.130
2025-07-02 05:48:59.136 # pump out diffs from after the synch point
2025-07-02 05:48:59.143 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:48:59.151
2025-07-02 05:48:59.162 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:48:59.171 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:59.179
2025-07-02 05:48:59.192 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:59.200 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:59.207 alo = 226, ahi = 1101
2025-07-02 05:48:59.221 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:59.230 blo = 226, bhi = 1101
2025-07-02 05:48:59.242
2025-07-02 05:48:59.257 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:59.266 g = []
2025-07-02 05:48:59.273 if alo < ahi:
2025-07-02 05:48:59.285 if blo < bhi:
2025-07-02 05:48:59.299 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:59.310 else:
2025-07-02 05:48:59.319 g = self._dump('-', a, alo, ahi)
2025-07-02 05:48:59.328 elif blo < bhi:
2025-07-02 05:48:59.336 g = self._dump('+', b, blo, bhi)
2025-07-02 05:48:59.343
2025-07-02 05:48:59.353 >       yield from g
2025-07-02 05:48:59.365
2025-07-02 05:48:59.376 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:48:59.388 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:48:59.399
2025-07-02 05:48:59.407 self = <difflib.Differ object at [hex]>
2025-07-02 05:48:59.415 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:48:59.423 alo = 226, ahi = 1101
2025-07-02 05:48:59.437 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:48:59.449 blo = 226, bhi = 1101
2025-07-02 05:48:59.460
2025-07-02 05:48:59.470 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:48:59.478 r"""
2025-07-02 05:48:59.487 When replacing one block of lines with another, search the blocks
2025-07-02 05:48:59.495 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:48:59.504 synch point, and intraline difference marking is done on the
2025-07-02 05:48:59.514 similar pair. Lots of work, but often worth it.
2025-07-02 05:48:59.521
2025-07-02 05:48:59.528 Example:
2025-07-02 05:48:59.535
2025-07-02 05:48:59.544 >>> d = Differ()
2025-07-02 05:48:59.552 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:48:59.560 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:48:59.567 >>> print(''.join(results), end="")
2025-07-02 05:48:59.578 - abcDefghiJkl
2025-07-02 05:48:59.602 + abcdefGhijkl
2025-07-02 05:48:59.619 """
2025-07-02 05:48:59.626
2025-07-02 05:48:59.636 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:48:59.647 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:48:59.655 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:48:59.663 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:48:59.669 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:48:59.677
2025-07-02 05:48:59.691 # search for the pair that matches best without being identical
2025-07-02 05:48:59.702 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:48:59.713 # on junk -- unless we have to)
2025-07-02 05:48:59.723 for j in range(blo, bhi):
2025-07-02 05:48:59.733 bj = b[j]
2025-07-02 05:48:59.745 cruncher.set_seq2(bj)
2025-07-02 05:48:59.754 for i in range(alo, ahi):
2025-07-02 05:48:59.761 ai = a[i]
2025-07-02 05:48:59.767 if ai == bj:
2025-07-02 05:48:59.772 if eqi is None:
2025-07-02 05:48:59.776 eqi, eqj = i, j
2025-07-02 05:48:59.781 continue
2025-07-02 05:48:59.786 cruncher.set_seq1(ai)
2025-07-02 05:48:59.793 # computing similarity is expensive, so use the quick
2025-07-02 05:48:59.799 # upper bounds first -- have seen this speed up messy
2025-07-02 05:48:59.814 # compares by a factor of 3.
2025-07-02 05:48:59.825 # note that ratio() is only expensive to compute the first
2025-07-02 05:48:59.835 # time it's called on a sequence pair; the expensive part
2025-07-02 05:48:59.844 # of the computation is cached by cruncher
2025-07-02 05:48:59.851 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:48:59.859 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:48:59.871 cruncher.ratio() > best_ratio:
2025-07-02 05:48:59.880 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:48:59.893 if best_ratio < cutoff:
2025-07-02 05:48:59.902 # no non-identical "pretty close" pair
2025-07-02 05:48:59.911 if eqi is None:
2025-07-02 05:48:59.922 # no identical pair either -- treat it as a straight replace
2025-07-02 05:48:59.932 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:48:59.940 return
2025-07-02 05:48:59.947 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:48:59.953 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:48:59.959 else:
2025-07-02 05:48:59.964 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:48:59.973 eqi = None
2025-07-02 05:48:59.986
2025-07-02 05:48:59.994 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:00.000 # identical
2025-07-02 05:49:00.005
2025-07-02 05:49:00.009 # pump out diffs from before the synch point
2025-07-02 05:49:00.016 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:00.025
2025-07-02 05:49:00.032 # do intraline marking on the synch pair
2025-07-02 05:49:00.039 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:00.048 if eqi is None:
2025-07-02 05:49:00.057 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:00.063 atags = btags = ""
2025-07-02 05:49:00.072 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:00.086 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:00.095 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:00.103 if tag == 'replace':
2025-07-02 05:49:00.109 atags += '^' * la
2025-07-02 05:49:00.122 btags += '^' * lb
2025-07-02 05:49:00.131 elif tag == 'delete':
2025-07-02 05:49:00.137 atags += '-' * la
2025-07-02 05:49:00.143 elif tag == 'insert':
2025-07-02 05:49:00.149 btags += '+' * lb
2025-07-02 05:49:00.155 elif tag == 'equal':
2025-07-02 05:49:00.160 atags += ' ' * la
2025-07-02 05:49:00.165 btags += ' ' * lb
2025-07-02 05:49:00.170 else:
2025-07-02 05:49:00.176 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:00.181 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:00.188 else:
2025-07-02 05:49:00.199 # the synch pair is identical
2025-07-02 05:49:00.208 yield '  ' + aelt
2025-07-02 05:49:00.216
2025-07-02 05:49:00.224 # pump out diffs from after the synch point
2025-07-02 05:49:00.231 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:00.250
2025-07-02 05:49:00.262 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:00.269 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:00.277
2025-07-02 05:49:00.282 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:00.288 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:00.293 alo = 227, ahi = 1101
2025-07-02 05:49:00.300 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:00.306 blo = 227, bhi = 1101
2025-07-02 05:49:00.311
2025-07-02 05:49:00.319 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:00.329 g = []
2025-07-02 05:49:00.337 if alo < ahi:
2025-07-02 05:49:00.345 if blo < bhi:
2025-07-02 05:49:00.356 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:00.366 else:
2025-07-02 05:49:00.379 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:00.390 elif blo < bhi:
2025-07-02 05:49:00.402 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:00.411
2025-07-02 05:49:00.419 >       yield from g
2025-07-02 05:49:00.426
2025-07-02 05:49:00.431 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:00.436 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:00.443
2025-07-02 05:49:00.454 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:00.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:00.476 alo = 227, ahi = 1101
2025-07-02 05:49:00.483 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:00.500 blo = 227, bhi = 1101
2025-07-02 05:49:00.511
2025-07-02 05:49:00.520 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:00.534 r"""
2025-07-02 05:49:00.545 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:00.557 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:00.568 synch point, and intraline difference marking is done on the
2025-07-02 05:49:00.576 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:00.582
2025-07-02 05:49:00.594 Example:
2025-07-02 05:49:00.604
2025-07-02 05:49:00.611 >>> d = Differ()
2025-07-02 05:49:00.617 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:00.623 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:00.631 >>> print(''.join(results), end="")
2025-07-02 05:49:00.642 - abcDefghiJkl
2025-07-02 05:49:00.658 + abcdefGhijkl
2025-07-02 05:49:00.679 """
2025-07-02 05:49:00.690
2025-07-02 05:49:00.699 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:00.711 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:00.721 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:00.732 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:00.743 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:00.752
2025-07-02 05:49:00.758 # search for the pair that matches best without being identical
2025-07-02 05:49:00.765 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:00.772 # on junk -- unless we have to)
2025-07-02 05:49:00.778 for j in range(blo, bhi):
2025-07-02 05:49:00.785 bj = b[j]
2025-07-02 05:49:00.791 cruncher.set_seq2(bj)
2025-07-02 05:49:00.800 for i in range(alo, ahi):
2025-07-02 05:49:00.811 ai = a[i]
2025-07-02 05:49:00.820 if ai == bj:
2025-07-02 05:49:00.829 if eqi is None:
2025-07-02 05:49:00.836 eqi, eqj = i, j
2025-07-02 05:49:00.842 continue
2025-07-02 05:49:00.853 cruncher.set_seq1(ai)
2025-07-02 05:49:00.864 # computing similarity is expensive, so use the quick
2025-07-02 05:49:00.872 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:00.880 # compares by a factor of 3.
2025-07-02 05:49:00.887 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:00.894 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:00.900 # of the computation is cached by cruncher
2025-07-02 05:49:00.906 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:00.917 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:00.927 cruncher.ratio() > best_ratio:
2025-07-02 05:49:00.939 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:00.950 if best_ratio < cutoff:
2025-07-02 05:49:00.962 # no non-identical "pretty close" pair
2025-07-02 05:49:00.972 if eqi is None:
2025-07-02 05:49:00.980 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:00.987 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:00.993 return
2025-07-02 05:49:00.999 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:01.006 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:01.016 else:
2025-07-02 05:49:01.028 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:01.041 eqi = None
2025-07-02 05:49:01.052
2025-07-02 05:49:01.060 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:01.071 # identical
2025-07-02 05:49:01.081
2025-07-02 05:49:01.088 # pump out diffs from before the synch point
2025-07-02 05:49:01.096 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:01.103
2025-07-02 05:49:01.109 # do intraline marking on the synch pair
2025-07-02 05:49:01.116 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:01.122 if eqi is None:
2025-07-02 05:49:01.134 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:01.145 atags = btags = ""
2025-07-02 05:49:01.156 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:01.166 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:01.179 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:01.190 if tag == 'replace':
2025-07-02 05:49:01.198 atags += '^' * la
2025-07-02 05:49:01.206 btags += '^' * lb
2025-07-02 05:49:01.215 elif tag == 'delete':
2025-07-02 05:49:01.222 atags += '-' * la
2025-07-02 05:49:01.228 elif tag == 'insert':
2025-07-02 05:49:01.234 btags += '+' * lb
2025-07-02 05:49:01.240 elif tag == 'equal':
2025-07-02 05:49:01.246 atags += ' ' * la
2025-07-02 05:49:01.254 btags += ' ' * lb
2025-07-02 05:49:01.263 else:
2025-07-02 05:49:01.270 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:01.276 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:01.288 else:
2025-07-02 05:49:01.299 # the synch pair is identical
2025-07-02 05:49:01.312 yield '  ' + aelt
2025-07-02 05:49:01.321
2025-07-02 05:49:01.328 # pump out diffs from after the synch point
2025-07-02 05:49:01.335 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:01.341
2025-07-02 05:49:01.347 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:01.356 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:01.366
2025-07-02 05:49:01.377 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:01.387 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:01.394 alo = 228, ahi = 1101
2025-07-02 05:49:01.404 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:01.412 blo = 228, bhi = 1101
2025-07-02 05:49:01.424
2025-07-02 05:49:01.437 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:01.450 g = []
2025-07-02 05:49:01.459 if alo < ahi:
2025-07-02 05:49:01.467 if blo < bhi:
2025-07-02 05:49:01.474 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:01.487 else:
2025-07-02 05:49:01.500 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:01.511 elif blo < bhi:
2025-07-02 05:49:01.526 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:01.538
2025-07-02 05:49:01.545 >       yield from g
2025-07-02 05:49:01.551
2025-07-02 05:49:01.557 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:01.568 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:01.576
2025-07-02 05:49:01.583 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:01.597 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:01.608 alo = 228, ahi = 1101
2025-07-02 05:49:01.621 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:01.632 blo = 228, bhi = 1101
2025-07-02 05:49:01.646
2025-07-02 05:49:01.659 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:01.668 r"""
2025-07-02 05:49:01.675 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:01.683 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:01.690 synch point, and intraline difference marking is done on the
2025-07-02 05:49:01.702 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:01.712
2025-07-02 05:49:01.721 Example:
2025-07-02 05:49:01.728
2025-07-02 05:49:01.735 >>> d = Differ()
2025-07-02 05:49:01.743 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:01.753 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:01.760 >>> print(''.join(results), end="")
2025-07-02 05:49:01.767 - abcDefghiJkl
2025-07-02 05:49:01.782 + abcdefGhijkl
2025-07-02 05:49:01.795 """
2025-07-02 05:49:01.799
2025-07-02 05:49:01.805 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:01.812 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:01.818 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:01.824 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:01.829 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:01.834
2025-07-02 05:49:01.841 # search for the pair that matches best without being identical
2025-07-02 05:49:01.851 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:01.859 # on junk -- unless we have to)
2025-07-02 05:49:01.867 for j in range(blo, bhi):
2025-07-02 05:49:01.874 bj = b[j]
2025-07-02 05:49:01.880 cruncher.set_seq2(bj)
2025-07-02 05:49:01.886 for i in range(alo, ahi):
2025-07-02 05:49:01.894 ai = a[i]
2025-07-02 05:49:01.900 if ai == bj:
2025-07-02 05:49:01.907 if eqi is None:
2025-07-02 05:49:01.914 eqi, eqj = i, j
2025-07-02 05:49:01.926 continue
2025-07-02 05:49:01.936 cruncher.set_seq1(ai)
2025-07-02 05:49:01.944 # computing similarity is expensive, so use the quick
2025-07-02 05:49:01.953 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:01.961 # compares by a factor of 3.
2025-07-02 05:49:01.969 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:01.978 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:01.993 # of the computation is cached by cruncher
2025-07-02 05:49:02.001 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:02.008 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:02.014 cruncher.ratio() > best_ratio:
2025-07-02 05:49:02.021 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:02.028 if best_ratio < cutoff:
2025-07-02 05:49:02.035 # no non-identical "pretty close" pair
2025-07-02 05:49:02.042 if eqi is None:
2025-07-02 05:49:02.051 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:02.059 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:02.066 return
2025-07-02 05:49:02.074 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:02.083 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:02.090 else:
2025-07-02 05:49:02.098 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:02.107 eqi = None
2025-07-02 05:49:02.114
2025-07-02 05:49:02.120 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:02.125 # identical
2025-07-02 05:49:02.131
2025-07-02 05:49:02.138 # pump out diffs from before the synch point
2025-07-02 05:49:02.148 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:02.159
2025-07-02 05:49:02.170 # do intraline marking on the synch pair
2025-07-02 05:49:02.183 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:02.195 if eqi is None:
2025-07-02 05:49:02.202 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:02.209 atags = btags = ""
2025-07-02 05:49:02.215 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:02.228 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:02.236 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:02.249 if tag == 'replace':
2025-07-02 05:49:02.260 atags += '^' * la
2025-07-02 05:49:02.267 btags += '^' * lb
2025-07-02 05:49:02.275 elif tag == 'delete':
2025-07-02 05:49:02.282 atags += '-' * la
2025-07-02 05:49:02.292 elif tag == 'insert':
2025-07-02 05:49:02.300 btags += '+' * lb
2025-07-02 05:49:02.307 elif tag == 'equal':
2025-07-02 05:49:02.317 atags += ' ' * la
2025-07-02 05:49:02.328 btags += ' ' * lb
2025-07-02 05:49:02.339 else:
2025-07-02 05:49:02.347 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:02.353 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:02.358 else:
2025-07-02 05:49:02.363 # the synch pair is identical
2025-07-02 05:49:02.367 yield '  ' + aelt
2025-07-02 05:49:02.372
2025-07-02 05:49:02.379 # pump out diffs from after the synch point
2025-07-02 05:49:02.386 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:02.391
2025-07-02 05:49:02.396 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:02.400 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:02.407
2025-07-02 05:49:02.413 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:02.423 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:02.432 alo = 229, ahi = 1101
2025-07-02 05:49:02.440 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:02.448 blo = 229, bhi = 1101
2025-07-02 05:49:02.453
2025-07-02 05:49:02.458 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:02.463 g = []
2025-07-02 05:49:02.473 if alo < ahi:
2025-07-02 05:49:02.483 if blo < bhi:
2025-07-02 05:49:02.491 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:02.499 else:
2025-07-02 05:49:02.511 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:02.518 elif blo < bhi:
2025-07-02 05:49:02.525 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:02.532
2025-07-02 05:49:02.539 >       yield from g
2025-07-02 05:49:02.553
2025-07-02 05:49:02.564 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:02.575 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:02.589
2025-07-02 05:49:02.600 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:02.608 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:02.613 alo = 229, ahi = 1101
2025-07-02 05:49:02.618 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:02.624 blo = 229, bhi = 1101
2025-07-02 05:49:02.630
2025-07-02 05:49:02.643 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:02.654 r"""
2025-07-02 05:49:02.662 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:02.668 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:02.674 synch point, and intraline difference marking is done on the
2025-07-02 05:49:02.681 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:02.689
2025-07-02 05:49:02.697 Example:
2025-07-02 05:49:02.704
2025-07-02 05:49:02.711 >>> d = Differ()
2025-07-02 05:49:02.717 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:02.722 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:02.727 >>> print(''.join(results), end="")
2025-07-02 05:49:02.732 - abcDefghiJkl
2025-07-02 05:49:02.749 + abcdefGhijkl
2025-07-02 05:49:02.769 """
2025-07-02 05:49:02.776
2025-07-02 05:49:02.784 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:02.790 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:02.799 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:02.809 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:02.816 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:02.823
2025-07-02 05:49:02.829 # search for the pair that matches best without being identical
2025-07-02 05:49:02.834 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:02.839 # on junk -- unless we have to)
2025-07-02 05:49:02.846 for j in range(blo, bhi):
2025-07-02 05:49:02.852 bj = b[j]
2025-07-02 05:49:02.859 cruncher.set_seq2(bj)
2025-07-02 05:49:02.869 for i in range(alo, ahi):
2025-07-02 05:49:02.877 ai = a[i]
2025-07-02 05:49:02.884 if ai == bj:
2025-07-02 05:49:02.893 if eqi is None:
2025-07-02 05:49:02.904 eqi, eqj = i, j
2025-07-02 05:49:02.912 continue
2025-07-02 05:49:02.918 cruncher.set_seq1(ai)
2025-07-02 05:49:02.924 # computing similarity is expensive, so use the quick
2025-07-02 05:49:02.931 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:02.936 # compares by a factor of 3.
2025-07-02 05:49:02.943 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:02.949 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:02.956 # of the computation is cached by cruncher
2025-07-02 05:49:02.962 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:02.973 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:02.982 cruncher.ratio() > best_ratio:
2025-07-02 05:49:02.995 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:03.009 if best_ratio < cutoff:
2025-07-02 05:49:03.022 # no non-identical "pretty close" pair
2025-07-02 05:49:03.029 if eqi is None:
2025-07-02 05:49:03.036 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:03.041 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:03.047 return
2025-07-02 05:49:03.054 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:03.061 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:03.067 else:
2025-07-02 05:49:03.072 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:03.077 eqi = None
2025-07-02 05:49:03.083
2025-07-02 05:49:03.089 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:03.100 # identical
2025-07-02 05:49:03.111
2025-07-02 05:49:03.120 # pump out diffs from before the synch point
2025-07-02 05:49:03.131 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:03.143
2025-07-02 05:49:03.156 # do intraline marking on the synch pair
2025-07-02 05:49:03.168 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:03.177 if eqi is None:
2025-07-02 05:49:03.189 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:03.200 atags = btags = ""
2025-07-02 05:49:03.214 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:03.242 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:03.249 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:03.256 if tag == 'replace':
2025-07-02 05:49:03.263 atags += '^' * la
2025-07-02 05:49:03.269 btags += '^' * lb
2025-07-02 05:49:03.275 elif tag == 'delete':
2025-07-02 05:49:03.284 atags += '-' * la
2025-07-02 05:49:03.294 elif tag == 'insert':
2025-07-02 05:49:03.303 btags += '+' * lb
2025-07-02 05:49:03.310 elif tag == 'equal':
2025-07-02 05:49:03.317 atags += ' ' * la
2025-07-02 05:49:03.324 btags += ' ' * lb
2025-07-02 05:49:03.332 else:
2025-07-02 05:49:03.340 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:03.347 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:03.354 else:
2025-07-02 05:49:03.364 # the synch pair is identical
2025-07-02 05:49:03.374 yield '  ' + aelt
2025-07-02 05:49:03.382
2025-07-02 05:49:03.389 # pump out diffs from after the synch point
2025-07-02 05:49:03.398 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:03.406
2025-07-02 05:49:03.416 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:03.429 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:03.439
2025-07-02 05:49:03.453 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:03.464 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:03.472 alo = 230, ahi = 1101
2025-07-02 05:49:03.482 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:03.492 blo = 230, bhi = 1101
2025-07-02 05:49:03.500
2025-07-02 05:49:03.509 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:03.517 g = []
2025-07-02 05:49:03.525 if alo < ahi:
2025-07-02 05:49:03.533 if blo < bhi:
2025-07-02 05:49:03.546 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:03.558 else:
2025-07-02 05:49:03.566 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:03.572 elif blo < bhi:
2025-07-02 05:49:03.580 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:03.587
2025-07-02 05:49:03.595 >       yield from g
2025-07-02 05:49:03.601
2025-07-02 05:49:03.609 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:03.617 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:03.624
2025-07-02 05:49:03.630 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:03.637 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:03.645 alo = 230, ahi = 1101
2025-07-02 05:49:03.651 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:03.658 blo = 230, bhi = 1101
2025-07-02 05:49:03.664
2025-07-02 05:49:03.672 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:03.679 r"""
2025-07-02 05:49:03.685 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:03.693 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:03.700 synch point, and intraline difference marking is done on the
2025-07-02 05:49:03.709 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:03.715
2025-07-02 05:49:03.722 Example:
2025-07-02 05:49:03.728
2025-07-02 05:49:03.734 >>> d = Differ()
2025-07-02 05:49:03.741 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:03.749 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:03.757 >>> print(''.join(results), end="")
2025-07-02 05:49:03.765 - abcDefghiJkl
2025-07-02 05:49:03.778 + abcdefGhijkl
2025-07-02 05:49:03.791 """
2025-07-02 05:49:03.798
2025-07-02 05:49:03.805 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:03.812 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:03.826 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:03.834 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:03.841 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:03.848
2025-07-02 05:49:03.856 # search for the pair that matches best without being identical
2025-07-02 05:49:03.864 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:03.872 # on junk -- unless we have to)
2025-07-02 05:49:03.880 for j in range(blo, bhi):
2025-07-02 05:49:03.886 bj = b[j]
2025-07-02 05:49:03.892 cruncher.set_seq2(bj)
2025-07-02 05:49:03.898 for i in range(alo, ahi):
2025-07-02 05:49:03.904 ai = a[i]
2025-07-02 05:49:03.910 if ai == bj:
2025-07-02 05:49:03.917 if eqi is None:
2025-07-02 05:49:03.923 eqi, eqj = i, j
2025-07-02 05:49:03.930 continue
2025-07-02 05:49:03.936 cruncher.set_seq1(ai)
2025-07-02 05:49:03.942 # computing similarity is expensive, so use the quick
2025-07-02 05:49:03.948 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:03.954 # compares by a factor of 3.
2025-07-02 05:49:03.960 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:03.965 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:03.971 # of the computation is cached by cruncher
2025-07-02 05:49:03.977 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:03.982 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:03.989 cruncher.ratio() > best_ratio:
2025-07-02 05:49:03.996 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:04.003 if best_ratio < cutoff:
2025-07-02 05:49:04.011 # no non-identical "pretty close" pair
2025-07-02 05:49:04.022 if eqi is None:
2025-07-02 05:49:04.032 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:04.040 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:04.047 return
2025-07-02 05:49:04.058 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:04.066 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:04.073 else:
2025-07-02 05:49:04.081 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:04.089 eqi = None
2025-07-02 05:49:04.096
2025-07-02 05:49:04.109 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:04.119 # identical
2025-07-02 05:49:04.126
2025-07-02 05:49:04.132 # pump out diffs from before the synch point
2025-07-02 05:49:04.139 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:04.146
2025-07-02 05:49:04.154 # do intraline marking on the synch pair
2025-07-02 05:49:04.163 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:04.170 if eqi is None:
2025-07-02 05:49:04.180 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:04.193 atags = btags = ""
2025-07-02 05:49:04.206 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:04.217 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:04.230 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:04.242 if tag == 'replace':
2025-07-02 05:49:04.248 atags += '^' * la
2025-07-02 05:49:04.253 btags += '^' * lb
2025-07-02 05:49:04.260 elif tag == 'delete':
2025-07-02 05:49:04.266 atags += '-' * la
2025-07-02 05:49:04.276 elif tag == 'insert':
2025-07-02 05:49:04.285 btags += '+' * lb
2025-07-02 05:49:04.291 elif tag == 'equal':
2025-07-02 05:49:04.296 atags += ' ' * la
2025-07-02 05:49:04.302 btags += ' ' * lb
2025-07-02 05:49:04.309 else:
2025-07-02 05:49:04.318 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:04.326 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:04.333 else:
2025-07-02 05:49:04.340 # the synch pair is identical
2025-07-02 05:49:04.346 yield '  ' + aelt
2025-07-02 05:49:04.353
2025-07-02 05:49:04.360 # pump out diffs from after the synch point
2025-07-02 05:49:04.368 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:04.382
2025-07-02 05:49:04.391 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:04.398 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:04.406
2025-07-02 05:49:04.412 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:04.420 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:04.426 alo = 231, ahi = 1101
2025-07-02 05:49:04.433 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:04.443 blo = 231, bhi = 1101
2025-07-02 05:49:04.451
2025-07-02 05:49:04.458 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:04.464 g = []
2025-07-02 05:49:04.469 if alo < ahi:
2025-07-02 05:49:04.475 if blo < bhi:
2025-07-02 05:49:04.482 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:04.494 else:
2025-07-02 05:49:04.507 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:04.518 elif blo < bhi:
2025-07-02 05:49:04.526 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:04.538
2025-07-02 05:49:04.548 >       yield from g
2025-07-02 05:49:04.562
2025-07-02 05:49:04.573 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:04.580 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:04.587
2025-07-02 05:49:04.595 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:04.605 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:04.612 alo = 231, ahi = 1101
2025-07-02 05:49:04.622 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:04.633 blo = 231, bhi = 1101
2025-07-02 05:49:04.643
2025-07-02 05:49:04.656 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:04.665 r"""
2025-07-02 05:49:04.672 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:04.679 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:04.687 synch point, and intraline difference marking is done on the
2025-07-02 05:49:04.699 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:04.709
2025-07-02 05:49:04.715 Example:
2025-07-02 05:49:04.721
2025-07-02 05:49:04.726 >>> d = Differ()
2025-07-02 05:49:04.732 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:04.740 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:04.746 >>> print(''.join(results), end="")
2025-07-02 05:49:04.752 - abcDefghiJkl
2025-07-02 05:49:04.765 + abcdefGhijkl
2025-07-02 05:49:04.786 """
2025-07-02 05:49:04.798
2025-07-02 05:49:04.808 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:04.817 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:04.824 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:04.837 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:04.848 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:04.855
2025-07-02 05:49:04.862 # search for the pair that matches best without being identical
2025-07-02 05:49:04.870 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:04.876 # on junk -- unless we have to)
2025-07-02 05:49:04.883 for j in range(blo, bhi):
2025-07-02 05:49:04.889 bj = b[j]
2025-07-02 05:49:04.895 cruncher.set_seq2(bj)
2025-07-02 05:49:04.903 for i in range(alo, ahi):
2025-07-02 05:49:04.914 ai = a[i]
2025-07-02 05:49:04.923 if ai == bj:
2025-07-02 05:49:04.931 if eqi is None:
2025-07-02 05:49:04.943 eqi, eqj = i, j
2025-07-02 05:49:04.955 continue
2025-07-02 05:49:04.968 cruncher.set_seq1(ai)
2025-07-02 05:49:04.980 # computing similarity is expensive, so use the quick
2025-07-02 05:49:04.992 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:05.000 # compares by a factor of 3.
2025-07-02 05:49:05.008 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:05.016 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:05.024 # of the computation is cached by cruncher
2025-07-02 05:49:05.032 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:05.040 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:05.047 cruncher.ratio() > best_ratio:
2025-07-02 05:49:05.060 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:05.070 if best_ratio < cutoff:
2025-07-02 05:49:05.077 # no non-identical "pretty close" pair
2025-07-02 05:49:05.085 if eqi is None:
2025-07-02 05:49:05.093 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:05.100 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:05.107 return
2025-07-02 05:49:05.115 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:05.122 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:05.133 else:
2025-07-02 05:49:05.143 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:05.152 eqi = None
2025-07-02 05:49:05.159
2025-07-02 05:49:05.165 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:05.170 # identical
2025-07-02 05:49:05.176
2025-07-02 05:49:05.183 # pump out diffs from before the synch point
2025-07-02 05:49:05.189 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:05.196
2025-07-02 05:49:05.202 # do intraline marking on the synch pair
2025-07-02 05:49:05.209 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:05.215 if eqi is None:
2025-07-02 05:49:05.221 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:05.226 atags = btags = ""
2025-07-02 05:49:05.233 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:05.241 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:05.248 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:05.256 if tag == 'replace':
2025-07-02 05:49:05.263 atags += '^' * la
2025-07-02 05:49:05.271 btags += '^' * lb
2025-07-02 05:49:05.279 elif tag == 'delete':
2025-07-02 05:49:05.292 atags += '-' * la
2025-07-02 05:49:05.300 elif tag == 'insert':
2025-07-02 05:49:05.307 btags += '+' * lb
2025-07-02 05:49:05.319 elif tag == 'equal':
2025-07-02 05:49:05.329 atags += ' ' * la
2025-07-02 05:49:05.336 btags += ' ' * lb
2025-07-02 05:49:05.344 else:
2025-07-02 05:49:05.352 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:05.360 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:05.368 else:
2025-07-02 05:49:05.375 # the synch pair is identical
2025-07-02 05:49:05.383 yield '  ' + aelt
2025-07-02 05:49:05.390
2025-07-02 05:49:05.398 # pump out diffs from after the synch point
2025-07-02 05:49:05.406 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:05.411
2025-07-02 05:49:05.416 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:05.421 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:05.426
2025-07-02 05:49:05.433 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:05.442 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:05.450 alo = 232, ahi = 1101
2025-07-02 05:49:05.459 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:05.470 blo = 232, bhi = 1101
2025-07-02 05:49:05.481
2025-07-02 05:49:05.493 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:05.503 g = []
2025-07-02 05:49:05.512 if alo < ahi:
2025-07-02 05:49:05.520 if blo < bhi:
2025-07-02 05:49:05.528 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:05.535 else:
2025-07-02 05:49:05.542 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:05.549 elif blo < bhi:
2025-07-02 05:49:05.560 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:05.570
2025-07-02 05:49:05.578 >       yield from g
2025-07-02 05:49:05.585
2025-07-02 05:49:05.592 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:05.598 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:05.603
2025-07-02 05:49:05.618 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:05.626 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:05.632 alo = 232, ahi = 1101
2025-07-02 05:49:05.641 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:05.646 blo = 232, bhi = 1101
2025-07-02 05:49:05.655
2025-07-02 05:49:05.663 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:05.670 r"""
2025-07-02 05:49:05.680 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:05.689 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:05.697 synch point, and intraline difference marking is done on the
2025-07-02 05:49:05.704 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:05.712
2025-07-02 05:49:05.722 Example:
2025-07-02 05:49:05.731
2025-07-02 05:49:05.740 >>> d = Differ()
2025-07-02 05:49:05.747 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:05.759 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:05.766 >>> print(''.join(results), end="")
2025-07-02 05:49:05.772 - abcDefghiJkl
2025-07-02 05:49:05.783 + abcdefGhijkl
2025-07-02 05:49:05.803 """
2025-07-02 05:49:05.809
2025-07-02 05:49:05.819 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:05.825 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:05.830 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:05.835 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:05.842 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:05.854
2025-07-02 05:49:05.862 # search for the pair that matches best without being identical
2025-07-02 05:49:05.869 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:05.875 # on junk -- unless we have to)
2025-07-02 05:49:05.881 for j in range(blo, bhi):
2025-07-02 05:49:05.885 bj = b[j]
2025-07-02 05:49:05.893 cruncher.set_seq2(bj)
2025-07-02 05:49:05.903 for i in range(alo, ahi):
2025-07-02 05:49:05.909 ai = a[i]
2025-07-02 05:49:05.916 if ai == bj:
2025-07-02 05:49:05.923 if eqi is None:
2025-07-02 05:49:05.930 eqi, eqj = i, j
2025-07-02 05:49:05.937 continue
2025-07-02 05:49:05.944 cruncher.set_seq1(ai)
2025-07-02 05:49:05.954 # computing similarity is expensive, so use the quick
2025-07-02 05:49:05.963 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:05.974 # compares by a factor of 3.
2025-07-02 05:49:05.985 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:05.996 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:06.004 # of the computation is cached by cruncher
2025-07-02 05:49:06.013 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:06.020 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:06.025 cruncher.ratio() > best_ratio:
2025-07-02 05:49:06.030 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:06.035 if best_ratio < cutoff:
2025-07-02 05:49:06.040 # no non-identical "pretty close" pair
2025-07-02 05:49:06.046 if eqi is None:
2025-07-02 05:49:06.056 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:06.066 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:06.077 return
2025-07-02 05:49:06.094 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:06.103 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:06.113 else:
2025-07-02 05:49:06.121 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:06.126 eqi = None
2025-07-02 05:49:06.132
2025-07-02 05:49:06.137 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:06.142 # identical
2025-07-02 05:49:06.147
2025-07-02 05:49:06.153 # pump out diffs from before the synch point
2025-07-02 05:49:06.159 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:06.166
2025-07-02 05:49:06.176 # do intraline marking on the synch pair
2025-07-02 05:49:06.184 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:06.191 if eqi is None:
2025-07-02 05:49:06.199 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:06.209 atags = btags = ""
2025-07-02 05:49:06.217 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:06.224 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:06.230 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:06.238 if tag == 'replace':
2025-07-02 05:49:06.242 atags += '^' * la
2025-07-02 05:49:06.247 btags += '^' * lb
2025-07-02 05:49:06.254 elif tag == 'delete':
2025-07-02 05:49:06.264 atags += '-' * la
2025-07-02 05:49:06.271 elif tag == 'insert':
2025-07-02 05:49:06.279 btags += '+' * lb
2025-07-02 05:49:06.289 elif tag == 'equal':
2025-07-02 05:49:06.300 atags += ' ' * la
2025-07-02 05:49:06.308 btags += ' ' * lb
2025-07-02 05:49:06.322 else:
2025-07-02 05:49:06.333 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:06.347 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:06.359 else:
2025-07-02 05:49:06.367 # the synch pair is identical
2025-07-02 05:49:06.373 yield '  ' + aelt
2025-07-02 05:49:06.379
2025-07-02 05:49:06.384 # pump out diffs from after the synch point
2025-07-02 05:49:06.391 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:06.400
2025-07-02 05:49:06.410 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:06.416 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:06.422
2025-07-02 05:49:06.427 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:06.433 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:06.443 alo = 233, ahi = 1101
2025-07-02 05:49:06.454 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:06.465 blo = 233, bhi = 1101
2025-07-02 05:49:06.474
2025-07-02 05:49:06.482 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:06.492 g = []
2025-07-02 05:49:06.501 if alo < ahi:
2025-07-02 05:49:06.509 if blo < bhi:
2025-07-02 05:49:06.515 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:06.520 else:
2025-07-02 05:49:06.526 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:06.531 elif blo < bhi:
2025-07-02 05:49:06.539 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:06.550
2025-07-02 05:49:06.559 >       yield from g
2025-07-02 05:49:06.566
2025-07-02 05:49:06.572 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:06.579 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:06.585
2025-07-02 05:49:06.591 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:06.596 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:06.601 alo = 233, ahi = 1101
2025-07-02 05:49:06.607 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:06.612 blo = 233, bhi = 1101
2025-07-02 05:49:06.617
2025-07-02 05:49:06.622 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:06.630 r"""
2025-07-02 05:49:06.640 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:06.648 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:06.656 synch point, and intraline difference marking is done on the
2025-07-02 05:49:06.666 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:06.677
2025-07-02 05:49:06.686 Example:
2025-07-02 05:49:06.694
2025-07-02 05:49:06.701 >>> d = Differ()
2025-07-02 05:49:06.709 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:06.715 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:06.721 >>> print(''.join(results), end="")
2025-07-02 05:49:06.733 - abcDefghiJkl
2025-07-02 05:49:06.752 + abcdefGhijkl
2025-07-02 05:49:06.766 """
2025-07-02 05:49:06.773
2025-07-02 05:49:06.780 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:06.786 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:06.791 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:06.796 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:06.802 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:06.807
2025-07-02 05:49:06.812 # search for the pair that matches best without being identical
2025-07-02 05:49:06.819 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:06.825 # on junk -- unless we have to)
2025-07-02 05:49:06.832 for j in range(blo, bhi):
2025-07-02 05:49:06.838 bj = b[j]
2025-07-02 05:49:06.844 cruncher.set_seq2(bj)
2025-07-02 05:49:06.850 for i in range(alo, ahi):
2025-07-02 05:49:06.856 ai = a[i]
2025-07-02 05:49:06.862 if ai == bj:
2025-07-02 05:49:06.867 if eqi is None:
2025-07-02 05:49:06.872 eqi, eqj = i, j
2025-07-02 05:49:06.878 continue
2025-07-02 05:49:06.888 cruncher.set_seq1(ai)
2025-07-02 05:49:06.897 # computing similarity is expensive, so use the quick
2025-07-02 05:49:06.905 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:06.911 # compares by a factor of 3.
2025-07-02 05:49:06.918 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:06.929 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:06.938 # of the computation is cached by cruncher
2025-07-02 05:49:06.945 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:06.952 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:06.960 cruncher.ratio() > best_ratio:
2025-07-02 05:49:06.967 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:06.973 if best_ratio < cutoff:
2025-07-02 05:49:06.983 # no non-identical "pretty close" pair
2025-07-02 05:49:06.994 if eqi is None:
2025-07-02 05:49:07.002 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:07.010 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:07.021 return
2025-07-02 05:49:07.030 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:07.038 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:07.051 else:
2025-07-02 05:49:07.060 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:07.071 eqi = None
2025-07-02 05:49:07.079
2025-07-02 05:49:07.086 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:07.093 # identical
2025-07-02 05:49:07.099
2025-07-02 05:49:07.107 # pump out diffs from before the synch point
2025-07-02 05:49:07.118 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:07.126
2025-07-02 05:49:07.133 # do intraline marking on the synch pair
2025-07-02 05:49:07.140 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:07.147 if eqi is None:
2025-07-02 05:49:07.157 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:07.168 atags = btags = ""
2025-07-02 05:49:07.176 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:07.183 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:07.191 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:07.203 if tag == 'replace':
2025-07-02 05:49:07.211 atags += '^' * la
2025-07-02 05:49:07.217 btags += '^' * lb
2025-07-02 05:49:07.223 elif tag == 'delete':
2025-07-02 05:49:07.229 atags += '-' * la
2025-07-02 05:49:07.236 elif tag == 'insert':
2025-07-02 05:49:07.243 btags += '+' * lb
2025-07-02 05:49:07.253 elif tag == 'equal':
2025-07-02 05:49:07.262 atags += ' ' * la
2025-07-02 05:49:07.272 btags += ' ' * lb
2025-07-02 05:49:07.283 else:
2025-07-02 05:49:07.296 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:07.307 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:07.315 else:
2025-07-02 05:49:07.323 # the synch pair is identical
2025-07-02 05:49:07.330 yield '  ' + aelt
2025-07-02 05:49:07.336
2025-07-02 05:49:07.342 # pump out diffs from after the synch point
2025-07-02 05:49:07.348 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:07.354
2025-07-02 05:49:07.368 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:07.375 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:07.385
2025-07-02 05:49:07.397 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:07.409 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:07.419 alo = 234, ahi = 1101
2025-07-02 05:49:07.429 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:07.437 blo = 234, bhi = 1101
2025-07-02 05:49:07.445
2025-07-02 05:49:07.452 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:07.463 g = []
2025-07-02 05:49:07.468 if alo < ahi:
2025-07-02 05:49:07.473 if blo < bhi:
2025-07-02 05:49:07.478 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:07.487 else:
2025-07-02 05:49:07.495 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:07.501 elif blo < bhi:
2025-07-02 05:49:07.507 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:07.512
2025-07-02 05:49:07.516 >       yield from g
2025-07-02 05:49:07.522
2025-07-02 05:49:07.527 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:07.539 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:07.547
2025-07-02 05:49:07.555 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:07.562 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:07.569 alo = 234, ahi = 1101
2025-07-02 05:49:07.579 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:07.590 blo = 234, bhi = 1101
2025-07-02 05:49:07.598
2025-07-02 05:49:07.605 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:07.616 r"""
2025-07-02 05:49:07.625 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:07.632 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:07.639 synch point, and intraline difference marking is done on the
2025-07-02 05:49:07.645 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:07.650
2025-07-02 05:49:07.659 Example:
2025-07-02 05:49:07.666
2025-07-02 05:49:07.671 >>> d = Differ()
2025-07-02 05:49:07.676 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:07.681 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:07.687 >>> print(''.join(results), end="")
2025-07-02 05:49:07.694 - abcDefghiJkl
2025-07-02 05:49:07.706 + abcdefGhijkl
2025-07-02 05:49:07.723 """
2025-07-02 05:49:07.730
2025-07-02 05:49:07.744 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:07.753 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:07.760 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:07.766 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:07.774 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:07.783
2025-07-02 05:49:07.789 # search for the pair that matches best without being identical
2025-07-02 05:49:07.796 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:07.803 # on junk -- unless we have to)
2025-07-02 05:49:07.811 for j in range(blo, bhi):
2025-07-02 05:49:07.822 bj = b[j]
2025-07-02 05:49:07.830 cruncher.set_seq2(bj)
2025-07-02 05:49:07.837 for i in range(alo, ahi):
2025-07-02 05:49:07.844 ai = a[i]
2025-07-02 05:49:07.850 if ai == bj:
2025-07-02 05:49:07.856 if eqi is None:
2025-07-02 05:49:07.862 eqi, eqj = i, j
2025-07-02 05:49:07.867 continue
2025-07-02 05:49:07.875 cruncher.set_seq1(ai)
2025-07-02 05:49:07.887 # computing similarity is expensive, so use the quick
2025-07-02 05:49:07.900 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:07.914 # compares by a factor of 3.
2025-07-02 05:49:07.926 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:07.937 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:07.947 # of the computation is cached by cruncher
2025-07-02 05:49:07.958 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:07.967 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:07.975 cruncher.ratio() > best_ratio:
2025-07-02 05:49:07.985 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:08.002 if best_ratio < cutoff:
2025-07-02 05:49:08.013 # no non-identical "pretty close" pair
2025-07-02 05:49:08.020 if eqi is None:
2025-07-02 05:49:08.027 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:08.035 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:08.043 return
2025-07-02 05:49:08.055 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:08.065 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:08.076 else:
2025-07-02 05:49:08.088 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:08.098 eqi = None
2025-07-02 05:49:08.111
2025-07-02 05:49:08.122 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:08.133 # identical
2025-07-02 05:49:08.147
2025-07-02 05:49:08.160 # pump out diffs from before the synch point
2025-07-02 05:49:08.172 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:08.185
2025-07-02 05:49:08.195 # do intraline marking on the synch pair
2025-07-02 05:49:08.206 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:08.219 if eqi is None:
2025-07-02 05:49:08.231 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:08.242 atags = btags = ""
2025-07-02 05:49:08.254 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:08.268 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:08.280 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:08.289 if tag == 'replace':
2025-07-02 05:49:08.297 atags += '^' * la
2025-07-02 05:49:08.304 btags += '^' * lb
2025-07-02 05:49:08.317 elif tag == 'delete':
2025-07-02 05:49:08.330 atags += '-' * la
2025-07-02 05:49:08.339 elif tag == 'insert':
2025-07-02 05:49:08.345 btags += '+' * lb
2025-07-02 05:49:08.350 elif tag == 'equal':
2025-07-02 05:49:08.358 atags += ' ' * la
2025-07-02 05:49:08.364 btags += ' ' * lb
2025-07-02 05:49:08.372 else:
2025-07-02 05:49:08.380 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:08.388 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:08.402 else:
2025-07-02 05:49:08.414 # the synch pair is identical
2025-07-02 05:49:08.423 yield '  ' + aelt
2025-07-02 05:49:08.430
2025-07-02 05:49:08.442 # pump out diffs from after the synch point
2025-07-02 05:49:08.452 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:08.461
2025-07-02 05:49:08.469 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:08.475 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:08.486
2025-07-02 05:49:08.493 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:08.502 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:08.511 alo = 235, ahi = 1101
2025-07-02 05:49:08.527 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:08.539 blo = 235, bhi = 1101
2025-07-02 05:49:08.550
2025-07-02 05:49:08.562 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:08.572 g = []
2025-07-02 05:49:08.580 if alo < ahi:
2025-07-02 05:49:08.586 if blo < bhi:
2025-07-02 05:49:08.594 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:08.609 else:
2025-07-02 05:49:08.619 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:08.631 elif blo < bhi:
2025-07-02 05:49:08.643 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:08.652
2025-07-02 05:49:08.660 >       yield from g
2025-07-02 05:49:08.666
2025-07-02 05:49:08.672 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:08.679 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:08.685
2025-07-02 05:49:08.691 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:08.698 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:08.704 alo = 235, ahi = 1101
2025-07-02 05:49:08.712 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:08.717 blo = 235, bhi = 1101
2025-07-02 05:49:08.721
2025-07-02 05:49:08.730 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:08.743 r"""
2025-07-02 05:49:08.751 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:08.757 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:08.764 synch point, and intraline difference marking is done on the
2025-07-02 05:49:08.769 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:08.775
2025-07-02 05:49:08.781 Example:
2025-07-02 05:49:08.788
2025-07-02 05:49:08.795 >>> d = Differ()
2025-07-02 05:49:08.802 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:08.811 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:08.822 >>> print(''.join(results), end="")
2025-07-02 05:49:08.830 - abcDefghiJkl
2025-07-02 05:49:08.844 + abcdefGhijkl
2025-07-02 05:49:08.857 """
2025-07-02 05:49:08.863
2025-07-02 05:49:08.871 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:08.884 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:08.896 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:08.907 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:08.917 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:08.928
2025-07-02 05:49:08.941 # search for the pair that matches best without being identical
2025-07-02 05:49:08.952 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:08.961 # on junk -- unless we have to)
2025-07-02 05:49:08.967 for j in range(blo, bhi):
2025-07-02 05:49:08.973 bj = b[j]
2025-07-02 05:49:08.977 cruncher.set_seq2(bj)
2025-07-02 05:49:08.982 for i in range(alo, ahi):
2025-07-02 05:49:08.987 ai = a[i]
2025-07-02 05:49:08.993 if ai == bj:
2025-07-02 05:49:08.998 if eqi is None:
2025-07-02 05:49:09.003 eqi, eqj = i, j
2025-07-02 05:49:09.008 continue
2025-07-02 05:49:09.013 cruncher.set_seq1(ai)
2025-07-02 05:49:09.018 # computing similarity is expensive, so use the quick
2025-07-02 05:49:09.023 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:09.028 # compares by a factor of 3.
2025-07-02 05:49:09.032 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:09.037 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:09.042 # of the computation is cached by cruncher
2025-07-02 05:49:09.047 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:09.052 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:09.056 cruncher.ratio() > best_ratio:
2025-07-02 05:49:09.062 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:09.067 if best_ratio < cutoff:
2025-07-02 05:49:09.073 # no non-identical "pretty close" pair
2025-07-02 05:49:09.079 if eqi is None:
2025-07-02 05:49:09.085 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:09.091 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:09.098 return
2025-07-02 05:49:09.109 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:09.118 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:09.124 else:
2025-07-02 05:49:09.131 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:09.137 eqi = None
2025-07-02 05:49:09.144
2025-07-02 05:49:09.155 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:09.163 # identical
2025-07-02 05:49:09.170
2025-07-02 05:49:09.179 # pump out diffs from before the synch point
2025-07-02 05:49:09.190 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:09.198
2025-07-02 05:49:09.209 # do intraline marking on the synch pair
2025-07-02 05:49:09.218 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:09.226 if eqi is None:
2025-07-02 05:49:09.233 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:09.239 atags = btags = ""
2025-07-02 05:49:09.245 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:09.251 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:09.259 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:09.266 if tag == 'replace':
2025-07-02 05:49:09.277 atags += '^' * la
2025-07-02 05:49:09.286 btags += '^' * lb
2025-07-02 05:49:09.294 elif tag == 'delete':
2025-07-02 05:49:09.302 atags += '-' * la
2025-07-02 05:49:09.309 elif tag == 'insert':
2025-07-02 05:49:09.316 btags += '+' * lb
2025-07-02 05:49:09.323 elif tag == 'equal':
2025-07-02 05:49:09.331 atags += ' ' * la
2025-07-02 05:49:09.343 btags += ' ' * lb
2025-07-02 05:49:09.352 else:
2025-07-02 05:49:09.361 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:09.366 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:09.371 else:
2025-07-02 05:49:09.377 # the synch pair is identical
2025-07-02 05:49:09.382 yield '  ' + aelt
2025-07-02 05:49:09.387
2025-07-02 05:49:09.392 # pump out diffs from after the synch point
2025-07-02 05:49:09.397 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:09.402
2025-07-02 05:49:09.407 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:09.412 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:09.416
2025-07-02 05:49:09.421 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:09.430 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:09.436 alo = 236, ahi = 1101
2025-07-02 05:49:09.443 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:09.449 blo = 236, bhi = 1101
2025-07-02 05:49:09.455
2025-07-02 05:49:09.463 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:09.475 g = []
2025-07-02 05:49:09.482 if alo < ahi:
2025-07-02 05:49:09.488 if blo < bhi:
2025-07-02 05:49:09.496 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:09.502 else:
2025-07-02 05:49:09.516 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:09.527 elif blo < bhi:
2025-07-02 05:49:09.539 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:09.551
2025-07-02 05:49:09.561 >       yield from g
2025-07-02 05:49:09.570
2025-07-02 05:49:09.577 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:09.584 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:09.591
2025-07-02 05:49:09.598 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:09.606 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:09.614 alo = 236, ahi = 1101
2025-07-02 05:49:09.626 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:09.634 blo = 236, bhi = 1101
2025-07-02 05:49:09.641
2025-07-02 05:49:09.646 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:09.651 r"""
2025-07-02 05:49:09.656 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:09.661 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:09.666 synch point, and intraline difference marking is done on the
2025-07-02 05:49:09.671 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:09.676
2025-07-02 05:49:09.680 Example:
2025-07-02 05:49:09.685
2025-07-02 05:49:09.690 >>> d = Differ()
2025-07-02 05:49:09.695 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:09.700 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:09.705 >>> print(''.join(results), end="")
2025-07-02 05:49:09.710 - abcDefghiJkl
2025-07-02 05:49:09.719 + abcdefGhijkl
2025-07-02 05:49:09.731 """
2025-07-02 05:49:09.736
2025-07-02 05:49:09.742 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:09.748 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:09.754 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:09.761 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:09.767 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:09.774
2025-07-02 05:49:09.784 # search for the pair that matches best without being identical
2025-07-02 05:49:09.791 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:09.797 # on junk -- unless we have to)
2025-07-02 05:49:09.803 for j in range(blo, bhi):
2025-07-02 05:49:09.809 bj = b[j]
2025-07-02 05:49:09.815 cruncher.set_seq2(bj)
2025-07-02 05:49:09.822 for i in range(alo, ahi):
2025-07-02 05:49:09.832 ai = a[i]
2025-07-02 05:49:09.844 if ai == bj:
2025-07-02 05:49:09.855 if eqi is None:
2025-07-02 05:49:09.862 eqi, eqj = i, j
2025-07-02 05:49:09.868 continue
2025-07-02 05:49:09.874 cruncher.set_seq1(ai)
2025-07-02 05:49:09.879 # computing similarity is expensive, so use the quick
2025-07-02 05:49:09.886 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:09.893 # compares by a factor of 3.
2025-07-02 05:49:09.899 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:09.906 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:09.914 # of the computation is cached by cruncher
2025-07-02 05:49:09.921 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:09.928 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:09.934 cruncher.ratio() > best_ratio:
2025-07-02 05:49:09.941 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:09.948 if best_ratio < cutoff:
2025-07-02 05:49:09.954 # no non-identical "pretty close" pair
2025-07-02 05:49:09.961 if eqi is None:
2025-07-02 05:49:09.968 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:09.975 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:09.983 return
2025-07-02 05:49:09.991 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:10.003 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:10.015 else:
2025-07-02 05:49:10.024 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:10.033 eqi = None
2025-07-02 05:49:10.039
2025-07-02 05:49:10.045 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:10.053 # identical
2025-07-02 05:49:10.059
2025-07-02 05:49:10.065 # pump out diffs from before the synch point
2025-07-02 05:49:10.077 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:10.086
2025-07-02 05:49:10.098 # do intraline marking on the synch pair
2025-07-02 05:49:10.107 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:10.119 if eqi is None:
2025-07-02 05:49:10.130 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:10.136 atags = btags = ""
2025-07-02 05:49:10.142 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:10.147 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:10.161 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:10.169 if tag == 'replace':
2025-07-02 05:49:10.175 atags += '^' * la
2025-07-02 05:49:10.181 btags += '^' * lb
2025-07-02 05:49:10.186 elif tag == 'delete':
2025-07-02 05:49:10.191 atags += '-' * la
2025-07-02 05:49:10.196 elif tag == 'insert':
2025-07-02 05:49:10.207 btags += '+' * lb
2025-07-02 05:49:10.217 elif tag == 'equal':
2025-07-02 05:49:10.224 atags += ' ' * la
2025-07-02 05:49:10.231 btags += ' ' * lb
2025-07-02 05:49:10.238 else:
2025-07-02 05:49:10.247 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:10.260 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:10.269 else:
2025-07-02 05:49:10.276 # the synch pair is identical
2025-07-02 05:49:10.283 yield '  ' + aelt
2025-07-02 05:49:10.289
2025-07-02 05:49:10.294 # pump out diffs from after the synch point
2025-07-02 05:49:10.299 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:10.304
2025-07-02 05:49:10.310 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:10.315 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:10.320
2025-07-02 05:49:10.325 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:10.331 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:10.338 alo = 237, ahi = 1101
2025-07-02 05:49:10.346 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:10.353 blo = 237, bhi = 1101
2025-07-02 05:49:10.359
2025-07-02 05:49:10.366 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:10.372 g = []
2025-07-02 05:49:10.380 if alo < ahi:
2025-07-02 05:49:10.388 if blo < bhi:
2025-07-02 05:49:10.396 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:10.403 else:
2025-07-02 05:49:10.410 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:10.419 elif blo < bhi:
2025-07-02 05:49:10.431 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:10.444
2025-07-02 05:49:10.453 >       yield from g
2025-07-02 05:49:10.460
2025-07-02 05:49:10.466 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:10.472 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:10.477
2025-07-02 05:49:10.484 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:10.491 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:10.498 alo = 237, ahi = 1101
2025-07-02 05:49:10.507 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:10.517 blo = 237, bhi = 1101
2025-07-02 05:49:10.525
2025-07-02 05:49:10.530 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:10.541 r"""
2025-07-02 05:49:10.547 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:10.554 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:10.562 synch point, and intraline difference marking is done on the
2025-07-02 05:49:10.572 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:10.580
2025-07-02 05:49:10.591 Example:
2025-07-02 05:49:10.600
2025-07-02 05:49:10.607 >>> d = Differ()
2025-07-02 05:49:10.613 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:10.625 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:10.632 >>> print(''.join(results), end="")
2025-07-02 05:49:10.639 - abcDefghiJkl
2025-07-02 05:49:10.655 + abcdefGhijkl
2025-07-02 05:49:10.676 """
2025-07-02 05:49:10.683
2025-07-02 05:49:10.693 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:10.707 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:10.717 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:10.725 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:10.732 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:10.739
2025-07-02 05:49:10.746 # search for the pair that matches best without being identical
2025-07-02 05:49:10.754 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:10.761 # on junk -- unless we have to)
2025-07-02 05:49:10.768 for j in range(blo, bhi):
2025-07-02 05:49:10.773 bj = b[j]
2025-07-02 05:49:10.780 cruncher.set_seq2(bj)
2025-07-02 05:49:10.786 for i in range(alo, ahi):
2025-07-02 05:49:10.792 ai = a[i]
2025-07-02 05:49:10.798 if ai == bj:
2025-07-02 05:49:10.807 if eqi is None:
2025-07-02 05:49:10.814 eqi, eqj = i, j
2025-07-02 05:49:10.822 continue
2025-07-02 05:49:10.834 cruncher.set_seq1(ai)
2025-07-02 05:49:10.844 # computing similarity is expensive, so use the quick
2025-07-02 05:49:10.855 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:10.869 # compares by a factor of 3.
2025-07-02 05:49:10.879 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:10.894 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:10.906 # of the computation is cached by cruncher
2025-07-02 05:49:10.915 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:10.923 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:10.931 cruncher.ratio() > best_ratio:
2025-07-02 05:49:10.939 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:10.945 if best_ratio < cutoff:
2025-07-02 05:49:10.956 # no non-identical "pretty close" pair
2025-07-02 05:49:10.967 if eqi is None:
2025-07-02 05:49:10.975 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:10.982 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:10.988 return
2025-07-02 05:49:10.995 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:11.000 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:11.009 else:
2025-07-02 05:49:11.016 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:11.023 eqi = None
2025-07-02 05:49:11.031
2025-07-02 05:49:11.039 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:11.047 # identical
2025-07-02 05:49:11.056
2025-07-02 05:49:11.064 # pump out diffs from before the synch point
2025-07-02 05:49:11.072 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:11.079
2025-07-02 05:49:11.088 # do intraline marking on the synch pair
2025-07-02 05:49:11.096 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:11.104 if eqi is None:
2025-07-02 05:49:11.111 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:11.119 atags = btags = ""
2025-07-02 05:49:11.127 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:11.134 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:11.141 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:11.148 if tag == 'replace':
2025-07-02 05:49:11.154 atags += '^' * la
2025-07-02 05:49:11.162 btags += '^' * lb
2025-07-02 05:49:11.170 elif tag == 'delete':
2025-07-02 05:49:11.180 atags += '-' * la
2025-07-02 05:49:11.192 elif tag == 'insert':
2025-07-02 05:49:11.201 btags += '+' * lb
2025-07-02 05:49:11.209 elif tag == 'equal':
2025-07-02 05:49:11.216 atags += ' ' * la
2025-07-02 05:49:11.222 btags += ' ' * lb
2025-07-02 05:49:11.232 else:
2025-07-02 05:49:11.244 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:11.252 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:11.261 else:
2025-07-02 05:49:11.269 # the synch pair is identical
2025-07-02 05:49:11.281 yield '  ' + aelt
2025-07-02 05:49:11.291
2025-07-02 05:49:11.299 # pump out diffs from after the synch point
2025-07-02 05:49:11.308 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:11.315
2025-07-02 05:49:11.321 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:11.327 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:11.333
2025-07-02 05:49:11.338 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:11.343 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:11.350 alo = 238, ahi = 1101
2025-07-02 05:49:11.356 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:11.362 blo = 238, bhi = 1101
2025-07-02 05:49:11.368
2025-07-02 05:49:11.374 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:11.382 g = []
2025-07-02 05:49:11.389 if alo < ahi:
2025-07-02 05:49:11.396 if blo < bhi:
2025-07-02 05:49:11.404 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:11.411 else:
2025-07-02 05:49:11.418 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:11.425 elif blo < bhi:
2025-07-02 05:49:11.433 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:11.441
2025-07-02 05:49:11.452 >       yield from g
2025-07-02 05:49:11.461
2025-07-02 05:49:11.470 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:11.483 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:11.492
2025-07-02 05:49:11.505 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:11.517 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:11.525 alo = 238, ahi = 1101
2025-07-02 05:49:11.534 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:11.544 blo = 238, bhi = 1101
2025-07-02 05:49:11.557
2025-07-02 05:49:11.567 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:11.573 r"""
2025-07-02 05:49:11.583 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:11.594 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:11.603 synch point, and intraline difference marking is done on the
2025-07-02 05:49:11.611 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:11.618
2025-07-02 05:49:11.625 Example:
2025-07-02 05:49:11.632
2025-07-02 05:49:11.639 >>> d = Differ()
2025-07-02 05:49:11.646 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:11.651 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:11.660 >>> print(''.join(results), end="")
2025-07-02 05:49:11.669 - abcDefghiJkl
2025-07-02 05:49:11.682 + abcdefGhijkl
2025-07-02 05:49:11.693 """
2025-07-02 05:49:11.699
2025-07-02 05:49:11.708 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:11.718 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:11.725 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:11.733 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:11.740 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:11.746
2025-07-02 05:49:11.759 # search for the pair that matches best without being identical
2025-07-02 05:49:11.770 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:11.779 # on junk -- unless we have to)
2025-07-02 05:49:11.789 for j in range(blo, bhi):
2025-07-02 05:49:11.797 bj = b[j]
2025-07-02 05:49:11.809 cruncher.set_seq2(bj)
2025-07-02 05:49:11.820 for i in range(alo, ahi):
2025-07-02 05:49:11.828 ai = a[i]
2025-07-02 05:49:11.834 if ai == bj:
2025-07-02 05:49:11.839 if eqi is None:
2025-07-02 05:49:11.844 eqi, eqj = i, j
2025-07-02 05:49:11.855 continue
2025-07-02 05:49:11.861 cruncher.set_seq1(ai)
2025-07-02 05:49:11.867 # computing similarity is expensive, so use the quick
2025-07-02 05:49:11.876 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:11.886 # compares by a factor of 3.
2025-07-02 05:49:11.897 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:11.909 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:11.917 # of the computation is cached by cruncher
2025-07-02 05:49:11.924 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:11.930 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:11.936 cruncher.ratio() > best_ratio:
2025-07-02 05:49:11.942 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:11.947 if best_ratio < cutoff:
2025-07-02 05:49:11.953 # no non-identical "pretty close" pair
2025-07-02 05:49:11.959 if eqi is None:
2025-07-02 05:49:11.965 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:11.971 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:11.979 return
2025-07-02 05:49:11.992 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:12.001 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:12.010 else:
2025-07-02 05:49:12.017 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:12.022 eqi = None
2025-07-02 05:49:12.027
2025-07-02 05:49:12.034 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:12.040 # identical
2025-07-02 05:49:12.052
2025-07-02 05:49:12.063 # pump out diffs from before the synch point
2025-07-02 05:49:12.072 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:12.080
2025-07-02 05:49:12.088 # do intraline marking on the synch pair
2025-07-02 05:49:12.095 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:12.102 if eqi is None:
2025-07-02 05:49:12.114 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:12.124 atags = btags = ""
2025-07-02 05:49:12.133 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:12.140 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:12.146 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:12.154 if tag == 'replace':
2025-07-02 05:49:12.161 atags += '^' * la
2025-07-02 05:49:12.167 btags += '^' * lb
2025-07-02 05:49:12.175 elif tag == 'delete':
2025-07-02 05:49:12.183 atags += '-' * la
2025-07-02 05:49:12.191 elif tag == 'insert':
2025-07-02 05:49:12.202 btags += '+' * lb
2025-07-02 05:49:12.210 elif tag == 'equal':
2025-07-02 05:49:12.218 atags += ' ' * la
2025-07-02 05:49:12.226 btags += ' ' * lb
2025-07-02 05:49:12.234 else:
2025-07-02 05:49:12.244 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:12.252 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:12.260 else:
2025-07-02 05:49:12.268 # the synch pair is identical
2025-07-02 05:49:12.277 yield '  ' + aelt
2025-07-02 05:49:12.290
2025-07-02 05:49:12.300 # pump out diffs from after the synch point
2025-07-02 05:49:12.307 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:12.313
2025-07-02 05:49:12.320 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:12.326 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:12.340
2025-07-02 05:49:12.353 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:12.366 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:12.376 alo = 239, ahi = 1101
2025-07-02 05:49:12.389 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:12.400 blo = 239, bhi = 1101
2025-07-02 05:49:12.411
2025-07-02 05:49:12.424 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:12.433 g = []
2025-07-02 05:49:12.442 if alo < ahi:
2025-07-02 05:49:12.448 if blo < bhi:
2025-07-02 05:49:12.455 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:12.462 else:
2025-07-02 05:49:12.474 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:12.485 elif blo < bhi:
2025-07-02 05:49:12.498 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:12.510
2025-07-02 05:49:12.519 >       yield from g
2025-07-02 05:49:12.530
2025-07-02 05:49:12.541 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:12.552 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:12.559
2025-07-02 05:49:12.567 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:12.575 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:12.584 alo = 239, ahi = 1101
2025-07-02 05:49:12.600 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:12.610 blo = 239, bhi = 1101
2025-07-02 05:49:12.617
2025-07-02 05:49:12.622 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:12.627 r"""
2025-07-02 05:49:12.632 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:12.642 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:12.649 synch point, and intraline difference marking is done on the
2025-07-02 05:49:12.656 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:12.664
2025-07-02 05:49:12.670 Example:
2025-07-02 05:49:12.682
2025-07-02 05:49:12.691 >>> d = Differ()
2025-07-02 05:49:12.700 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:12.706 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:12.713 >>> print(''.join(results), end="")
2025-07-02 05:49:12.719 - abcDefghiJkl
2025-07-02 05:49:12.739 + abcdefGhijkl
2025-07-02 05:49:12.758 """
2025-07-02 05:49:12.768
2025-07-02 05:49:12.780 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:12.793 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:12.806 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:12.818 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:12.833 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:12.844
2025-07-02 05:49:12.856 # search for the pair that matches best without being identical
2025-07-02 05:49:12.868 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:12.879 # on junk -- unless we have to)
2025-07-02 05:49:12.889 for j in range(blo, bhi):
2025-07-02 05:49:12.902 bj = b[j]
2025-07-02 05:49:12.913 cruncher.set_seq2(bj)
2025-07-02 05:49:12.924 for i in range(alo, ahi):
2025-07-02 05:49:12.935 ai = a[i]
2025-07-02 05:49:12.948 if ai == bj:
2025-07-02 05:49:12.960 if eqi is None:
2025-07-02 05:49:12.971 eqi, eqj = i, j
2025-07-02 05:49:12.981 continue
2025-07-02 05:49:12.988 cruncher.set_seq1(ai)
2025-07-02 05:49:12.996 # computing similarity is expensive, so use the quick
2025-07-02 05:49:13.003 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:13.014 # compares by a factor of 3.
2025-07-02 05:49:13.025 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:13.037 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:13.046 # of the computation is cached by cruncher
2025-07-02 05:49:13.055 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:13.063 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:13.070 cruncher.ratio() > best_ratio:
2025-07-02 05:49:13.081 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:13.093 if best_ratio < cutoff:
2025-07-02 05:49:13.105 # no non-identical "pretty close" pair
2025-07-02 05:49:13.116 if eqi is None:
2025-07-02 05:49:13.129 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:13.141 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:13.151 return
2025-07-02 05:49:13.157 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:13.164 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:13.171 else:
2025-07-02 05:49:13.180 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:13.189 eqi = None
2025-07-02 05:49:13.196
2025-07-02 05:49:13.203 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:13.211 # identical
2025-07-02 05:49:13.223
2025-07-02 05:49:13.235 # pump out diffs from before the synch point
2025-07-02 05:49:13.246 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:13.256
2025-07-02 05:49:13.264 # do intraline marking on the synch pair
2025-07-02 05:49:13.270 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:13.277 if eqi is None:
2025-07-02 05:49:13.285 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:13.292 atags = btags = ""
2025-07-02 05:49:13.300 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:13.306 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:13.313 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:13.319 if tag == 'replace':
2025-07-02 05:49:13.327 atags += '^' * la
2025-07-02 05:49:13.336 btags += '^' * lb
2025-07-02 05:49:13.350 elif tag == 'delete':
2025-07-02 05:49:13.362 atags += '-' * la
2025-07-02 05:49:13.369 elif tag == 'insert':
2025-07-02 05:49:13.376 btags += '+' * lb
2025-07-02 05:49:13.383 elif tag == 'equal':
2025-07-02 05:49:13.389 atags += ' ' * la
2025-07-02 05:49:13.395 btags += ' ' * lb
2025-07-02 05:49:13.401 else:
2025-07-02 05:49:13.407 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:13.414 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:13.424 else:
2025-07-02 05:49:13.431 # the synch pair is identical
2025-07-02 05:49:13.437 yield '  ' + aelt
2025-07-02 05:49:13.443
2025-07-02 05:49:13.451 # pump out diffs from after the synch point
2025-07-02 05:49:13.462 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:13.470
2025-07-02 05:49:13.477 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:13.486 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:13.499
2025-07-02 05:49:13.509 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:13.520 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:13.528 alo = 240, ahi = 1101
2025-07-02 05:49:13.537 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:13.544 blo = 240, bhi = 1101
2025-07-02 05:49:13.551
2025-07-02 05:49:13.558 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:13.566 g = []
2025-07-02 05:49:13.577 if alo < ahi:
2025-07-02 05:49:13.587 if blo < bhi:
2025-07-02 05:49:13.595 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:13.607 else:
2025-07-02 05:49:13.617 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:13.625 elif blo < bhi:
2025-07-02 05:49:13.637 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:13.646
2025-07-02 05:49:13.653 >       yield from g
2025-07-02 05:49:13.660
2025-07-02 05:49:13.666 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:13.673 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:13.678
2025-07-02 05:49:13.691 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:13.704 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:13.713 alo = 240, ahi = 1101
2025-07-02 05:49:13.722 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:13.733 blo = 240, bhi = 1101
2025-07-02 05:49:13.746
2025-07-02 05:49:13.758 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:13.771 r"""
2025-07-02 05:49:13.785 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:13.797 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:13.809 synch point, and intraline difference marking is done on the
2025-07-02 05:49:13.820 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:13.830
2025-07-02 05:49:13.841 Example:
2025-07-02 05:49:13.852
2025-07-02 05:49:13.860 >>> d = Differ()
2025-07-02 05:49:13.868 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:13.875 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:13.881 >>> print(''.join(results), end="")
2025-07-02 05:49:13.886 - abcDefghiJkl
2025-07-02 05:49:13.905 + abcdefGhijkl
2025-07-02 05:49:13.919 """
2025-07-02 05:49:13.924
2025-07-02 05:49:13.930 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:13.945 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:13.952 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:13.961 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:13.973 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:13.982
2025-07-02 05:49:13.992 # search for the pair that matches best without being identical
2025-07-02 05:49:14.005 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:14.017 # on junk -- unless we have to)
2025-07-02 05:49:14.030 for j in range(blo, bhi):
2025-07-02 05:49:14.039 bj = b[j]
2025-07-02 05:49:14.049 cruncher.set_seq2(bj)
2025-07-02 05:49:14.056 for i in range(alo, ahi):
2025-07-02 05:49:14.062 ai = a[i]
2025-07-02 05:49:14.067 if ai == bj:
2025-07-02 05:49:14.073 if eqi is None:
2025-07-02 05:49:14.079 eqi, eqj = i, j
2025-07-02 05:49:14.087 continue
2025-07-02 05:49:14.095 cruncher.set_seq1(ai)
2025-07-02 05:49:14.103 # computing similarity is expensive, so use the quick
2025-07-02 05:49:14.114 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:14.124 # compares by a factor of 3.
2025-07-02 05:49:14.131 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:14.138 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:14.149 # of the computation is cached by cruncher
2025-07-02 05:49:14.158 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:14.170 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:14.179 cruncher.ratio() > best_ratio:
2025-07-02 05:49:14.188 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:14.195 if best_ratio < cutoff:
2025-07-02 05:49:14.203 # no non-identical "pretty close" pair
2025-07-02 05:49:14.210 if eqi is None:
2025-07-02 05:49:14.217 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:14.223 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:14.230 return
2025-07-02 05:49:14.239 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:14.253 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:14.265 else:
2025-07-02 05:49:14.273 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:14.281 eqi = None
2025-07-02 05:49:14.295
2025-07-02 05:49:14.308 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:14.316 # identical
2025-07-02 05:49:14.327
2025-07-02 05:49:14.339 # pump out diffs from before the synch point
2025-07-02 05:49:14.350 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:14.361
2025-07-02 05:49:14.373 # do intraline marking on the synch pair
2025-07-02 05:49:14.381 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:14.393 if eqi is None:
2025-07-02 05:49:14.401 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:14.409 atags = btags = ""
2025-07-02 05:49:14.415 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:14.422 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:14.433 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:14.440 if tag == 'replace':
2025-07-02 05:49:14.447 atags += '^' * la
2025-07-02 05:49:14.453 btags += '^' * lb
2025-07-02 05:49:14.458 elif tag == 'delete':
2025-07-02 05:49:14.468 atags += '-' * la
2025-07-02 05:49:14.478 elif tag == 'insert':
2025-07-02 05:49:14.487 btags += '+' * lb
2025-07-02 05:49:14.495 elif tag == 'equal':
2025-07-02 05:49:14.503 atags += ' ' * la
2025-07-02 05:49:14.515 btags += ' ' * lb
2025-07-02 05:49:14.526 else:
2025-07-02 05:49:14.537 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:14.547 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:14.555 else:
2025-07-02 05:49:14.563 # the synch pair is identical
2025-07-02 05:49:14.570 yield '  ' + aelt
2025-07-02 05:49:14.576
2025-07-02 05:49:14.583 # pump out diffs from after the synch point
2025-07-02 05:49:14.592 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:14.599
2025-07-02 05:49:14.607 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:14.623 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:14.637
2025-07-02 05:49:14.648 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:14.661 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:14.673 alo = 241, ahi = 1101
2025-07-02 05:49:14.684 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:14.697 blo = 241, bhi = 1101
2025-07-02 05:49:14.707
2025-07-02 05:49:14.715 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:14.722 g = []
2025-07-02 05:49:14.728 if alo < ahi:
2025-07-02 05:49:14.741 if blo < bhi:
2025-07-02 05:49:14.754 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:14.766 else:
2025-07-02 05:49:14.780 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:14.790 elif blo < bhi:
2025-07-02 05:49:14.798 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:14.805
2025-07-02 05:49:14.812 >       yield from g
2025-07-02 05:49:14.818
2025-07-02 05:49:14.826 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:14.836 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:14.844
2025-07-02 05:49:14.852 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:14.865 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:14.875 alo = 241, ahi = 1101
2025-07-02 05:49:14.883 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:14.890 blo = 241, bhi = 1101
2025-07-02 05:49:14.896
2025-07-02 05:49:14.902 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:14.908 r"""
2025-07-02 05:49:14.915 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:14.923 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:14.930 synch point, and intraline difference marking is done on the
2025-07-02 05:49:14.938 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:14.947
2025-07-02 05:49:14.959 Example:
2025-07-02 05:49:14.968
2025-07-02 05:49:14.975 >>> d = Differ()
2025-07-02 05:49:14.982 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:14.989 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:14.996 >>> print(''.join(results), end="")
2025-07-02 05:49:15.002 - abcDefghiJkl
2025-07-02 05:49:15.018 + abcdefGhijkl
2025-07-02 05:49:15.039 """
2025-07-02 05:49:15.046
2025-07-02 05:49:15.054 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:15.061 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:15.068 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:15.075 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:15.083 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:15.090
2025-07-02 05:49:15.102 # search for the pair that matches best without being identical
2025-07-02 05:49:15.112 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:15.123 # on junk -- unless we have to)
2025-07-02 05:49:15.131 for j in range(blo, bhi):
2025-07-02 05:49:15.139 bj = b[j]
2025-07-02 05:49:15.152 cruncher.set_seq2(bj)
2025-07-02 05:49:15.162 for i in range(alo, ahi):
2025-07-02 05:49:15.174 ai = a[i]
2025-07-02 05:49:15.183 if ai == bj:
2025-07-02 05:49:15.190 if eqi is None:
2025-07-02 05:49:15.197 eqi, eqj = i, j
2025-07-02 05:49:15.204 continue
2025-07-02 05:49:15.211 cruncher.set_seq1(ai)
2025-07-02 05:49:15.219 # computing similarity is expensive, so use the quick
2025-07-02 05:49:15.233 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:15.242 # compares by a factor of 3.
2025-07-02 05:49:15.251 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:15.260 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:15.268 # of the computation is cached by cruncher
2025-07-02 05:49:15.276 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:15.284 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:15.293 cruncher.ratio() > best_ratio:
2025-07-02 05:49:15.304 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:15.314 if best_ratio < cutoff:
2025-07-02 05:49:15.322 # no non-identical "pretty close" pair
2025-07-02 05:49:15.330 if eqi is None:
2025-07-02 05:49:15.341 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:15.350 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:15.357 return
2025-07-02 05:49:15.363 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:15.370 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:15.376 else:
2025-07-02 05:49:15.384 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:15.392 eqi = None
2025-07-02 05:49:15.400
2025-07-02 05:49:15.407 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:15.415 # identical
2025-07-02 05:49:15.423
2025-07-02 05:49:15.432 # pump out diffs from before the synch point
2025-07-02 05:49:15.443 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:15.453
2025-07-02 05:49:15.461 # do intraline marking on the synch pair
2025-07-02 05:49:15.468 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:15.473 if eqi is None:
2025-07-02 05:49:15.480 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:15.486 atags = btags = ""
2025-07-02 05:49:15.492 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:15.499 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:15.506 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:15.514 if tag == 'replace':
2025-07-02 05:49:15.527 atags += '^' * la
2025-07-02 05:49:15.536 btags += '^' * lb
2025-07-02 05:49:15.543 elif tag == 'delete':
2025-07-02 05:49:15.549 atags += '-' * la
2025-07-02 05:49:15.555 elif tag == 'insert':
2025-07-02 05:49:15.567 btags += '+' * lb
2025-07-02 05:49:15.575 elif tag == 'equal':
2025-07-02 05:49:15.582 atags += ' ' * la
2025-07-02 05:49:15.589 btags += ' ' * lb
2025-07-02 05:49:15.595 else:
2025-07-02 05:49:15.602 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:15.612 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:15.622 else:
2025-07-02 05:49:15.630 # the synch pair is identical
2025-07-02 05:49:15.636 yield '  ' + aelt
2025-07-02 05:49:15.643
2025-07-02 05:49:15.651 # pump out diffs from after the synch point
2025-07-02 05:49:15.659 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:15.666
2025-07-02 05:49:15.674 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:15.681 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:15.688
2025-07-02 05:49:15.699 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:15.708 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:15.721 alo = 242, ahi = 1101
2025-07-02 05:49:15.734 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:15.745 blo = 242, bhi = 1101
2025-07-02 05:49:15.755
2025-07-02 05:49:15.768 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:15.779 g = []
2025-07-02 05:49:15.793 if alo < ahi:
2025-07-02 05:49:15.805 if blo < bhi:
2025-07-02 05:49:15.818 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:15.829 else:
2025-07-02 05:49:15.838 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:15.852 elif blo < bhi:
2025-07-02 05:49:15.865 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:15.878
2025-07-02 05:49:15.888 >       yield from g
2025-07-02 05:49:15.897
2025-07-02 05:49:15.905 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:15.912 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:15.919
2025-07-02 05:49:15.927 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:15.934 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:15.940 alo = 242, ahi = 1101
2025-07-02 05:49:15.947 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:15.954 blo = 242, bhi = 1101
2025-07-02 05:49:15.959
2025-07-02 05:49:15.966 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:15.975 r"""
2025-07-02 05:49:15.986 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:15.996 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:16.006 synch point, and intraline difference marking is done on the
2025-07-02 05:49:16.018 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:16.028
2025-07-02 05:49:16.041 Example:
2025-07-02 05:49:16.052
2025-07-02 05:49:16.063 >>> d = Differ()
2025-07-02 05:49:16.074 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:16.086 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:16.098 >>> print(''.join(results), end="")
2025-07-02 05:49:16.110 - abcDefghiJkl
2025-07-02 05:49:16.130 + abcdefGhijkl
2025-07-02 05:49:16.157 """
2025-07-02 05:49:16.170
2025-07-02 05:49:16.180 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:16.189 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:16.196 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:16.203 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:16.213 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:16.222
2025-07-02 05:49:16.231 # search for the pair that matches best without being identical
2025-07-02 05:49:16.241 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:16.253 # on junk -- unless we have to)
2025-07-02 05:49:16.262 for j in range(blo, bhi):
2025-07-02 05:49:16.270 bj = b[j]
2025-07-02 05:49:16.278 cruncher.set_seq2(bj)
2025-07-02 05:49:16.286 for i in range(alo, ahi):
2025-07-02 05:49:16.298 ai = a[i]
2025-07-02 05:49:16.310 if ai == bj:
2025-07-02 05:49:16.319 if eqi is None:
2025-07-02 05:49:16.327 eqi, eqj = i, j
2025-07-02 05:49:16.332 continue
2025-07-02 05:49:16.338 cruncher.set_seq1(ai)
2025-07-02 05:49:16.347 # computing similarity is expensive, so use the quick
2025-07-02 05:49:16.353 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:16.360 # compares by a factor of 3.
2025-07-02 05:49:16.367 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:16.375 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:16.383 # of the computation is cached by cruncher
2025-07-02 05:49:16.390 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:16.396 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:16.402 cruncher.ratio() > best_ratio:
2025-07-02 05:49:16.407 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:16.413 if best_ratio < cutoff:
2025-07-02 05:49:16.420 # no non-identical "pretty close" pair
2025-07-02 05:49:16.429 if eqi is None:
2025-07-02 05:49:16.442 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:16.452 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:16.463 return
2025-07-02 05:49:16.472 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:16.482 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:16.492 else:
2025-07-02 05:49:16.499 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:16.506 eqi = None
2025-07-02 05:49:16.517
2025-07-02 05:49:16.527 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:16.540 # identical
2025-07-02 05:49:16.551
2025-07-02 05:49:16.564 # pump out diffs from before the synch point
2025-07-02 05:49:16.575 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:16.583
2025-07-02 05:49:16.590 # do intraline marking on the synch pair
2025-07-02 05:49:16.597 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:16.604 if eqi is None:
2025-07-02 05:49:16.615 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:16.621 atags = btags = ""
2025-07-02 05:49:16.628 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:16.636 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:16.643 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:16.650 if tag == 'replace':
2025-07-02 05:49:16.662 atags += '^' * la
2025-07-02 05:49:16.670 btags += '^' * lb
2025-07-02 05:49:16.676 elif tag == 'delete':
2025-07-02 05:49:16.683 atags += '-' * la
2025-07-02 05:49:16.691 elif tag == 'insert':
2025-07-02 05:49:16.698 btags += '+' * lb
2025-07-02 05:49:16.712 elif tag == 'equal':
2025-07-02 05:49:16.726 atags += ' ' * la
2025-07-02 05:49:16.737 btags += ' ' * lb
2025-07-02 05:49:16.747 else:
2025-07-02 05:49:16.760 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:16.766 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:16.772 else:
2025-07-02 05:49:16.778 # the synch pair is identical
2025-07-02 05:49:16.784 yield '  ' + aelt
2025-07-02 05:49:16.791
2025-07-02 05:49:16.803 # pump out diffs from after the synch point
2025-07-02 05:49:16.811 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:16.818
2025-07-02 05:49:16.826 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:16.833 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:16.839
2025-07-02 05:49:16.845 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:16.853 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:16.860 alo = 243, ahi = 1101
2025-07-02 05:49:16.866 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:16.872 blo = 243, bhi = 1101
2025-07-02 05:49:16.880
2025-07-02 05:49:16.888 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:16.894 g = []
2025-07-02 05:49:16.906 if alo < ahi:
2025-07-02 05:49:16.918 if blo < bhi:
2025-07-02 05:49:16.930 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:16.939 else:
2025-07-02 05:49:16.948 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:16.959 elif blo < bhi:
2025-07-02 05:49:16.967 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:16.974
2025-07-02 05:49:16.981 >       yield from g
2025-07-02 05:49:16.987
2025-07-02 05:49:17.002 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:17.012 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:17.019
2025-07-02 05:49:17.027 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:17.034 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:17.041 alo = 243, ahi = 1101
2025-07-02 05:49:17.047 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:17.055 blo = 243, bhi = 1101
2025-07-02 05:49:17.066
2025-07-02 05:49:17.075 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:17.089 r"""
2025-07-02 05:49:17.100 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:17.111 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:17.118 synch point, and intraline difference marking is done on the
2025-07-02 05:49:17.129 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:17.143
2025-07-02 05:49:17.152 Example:
2025-07-02 05:49:17.159
2025-07-02 05:49:17.167 >>> d = Differ()
2025-07-02 05:49:17.174 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:17.187 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:17.199 >>> print(''.join(results), end="")
2025-07-02 05:49:17.210 - abcDefghiJkl
2025-07-02 05:49:17.235 + abcdefGhijkl
2025-07-02 05:49:17.251 """
2025-07-02 05:49:17.258
2025-07-02 05:49:17.266 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:17.275 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:17.286 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:17.293 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:17.299 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:17.305
2025-07-02 05:49:17.310 # search for the pair that matches best without being identical
2025-07-02 05:49:17.315 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:17.320 # on junk -- unless we have to)
2025-07-02 05:49:17.326 for j in range(blo, bhi):
2025-07-02 05:49:17.336 bj = b[j]
2025-07-02 05:49:17.346 cruncher.set_seq2(bj)
2025-07-02 05:49:17.358 for i in range(alo, ahi):
2025-07-02 05:49:17.370 ai = a[i]
2025-07-02 05:49:17.384 if ai == bj:
2025-07-02 05:49:17.396 if eqi is None:
2025-07-02 05:49:17.406 eqi, eqj = i, j
2025-07-02 05:49:17.420 continue
2025-07-02 05:49:17.427 cruncher.set_seq1(ai)
2025-07-02 05:49:17.434 # computing similarity is expensive, so use the quick
2025-07-02 05:49:17.441 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:17.450 # compares by a factor of 3.
2025-07-02 05:49:17.459 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:17.470 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:17.479 # of the computation is cached by cruncher
2025-07-02 05:49:17.487 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:17.494 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:17.501 cruncher.ratio() > best_ratio:
2025-07-02 05:49:17.507 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:17.516 if best_ratio < cutoff:
2025-07-02 05:49:17.527 # no non-identical "pretty close" pair
2025-07-02 05:49:17.540 if eqi is None:
2025-07-02 05:49:17.550 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:17.559 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:17.566 return
2025-07-02 05:49:17.573 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:17.578 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:17.584 else:
2025-07-02 05:49:17.592 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:17.598 eqi = None
2025-07-02 05:49:17.604
2025-07-02 05:49:17.610 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:17.617 # identical
2025-07-02 05:49:17.624
2025-07-02 05:49:17.632 # pump out diffs from before the synch point
2025-07-02 05:49:17.639 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:17.644
2025-07-02 05:49:17.654 # do intraline marking on the synch pair
2025-07-02 05:49:17.667 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:17.683 if eqi is None:
2025-07-02 05:49:17.694 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:17.703 atags = btags = ""
2025-07-02 05:49:17.710 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:17.716 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:17.721 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:17.726 if tag == 'replace':
2025-07-02 05:49:17.731 atags += '^' * la
2025-07-02 05:49:17.737 btags += '^' * lb
2025-07-02 05:49:17.748 elif tag == 'delete':
2025-07-02 05:49:17.754 atags += '-' * la
2025-07-02 05:49:17.764 elif tag == 'insert':
2025-07-02 05:49:17.775 btags += '+' * lb
2025-07-02 05:49:17.783 elif tag == 'equal':
2025-07-02 05:49:17.791 atags += ' ' * la
2025-07-02 05:49:17.799 btags += ' ' * lb
2025-07-02 05:49:17.806 else:
2025-07-02 05:49:17.814 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:17.826 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:17.837 else:
2025-07-02 05:49:17.845 # the synch pair is identical
2025-07-02 05:49:17.856 yield '  ' + aelt
2025-07-02 05:49:17.867
2025-07-02 05:49:17.878 # pump out diffs from after the synch point
2025-07-02 05:49:17.886 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:17.893
2025-07-02 05:49:17.901 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:17.908 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:17.915
2025-07-02 05:49:17.923 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:17.937 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:17.949 alo = 246, ahi = 1101
2025-07-02 05:49:17.959 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:17.969 blo = 246, bhi = 1101
2025-07-02 05:49:17.980
2025-07-02 05:49:17.989 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:18.003 g = []
2025-07-02 05:49:18.010 if alo < ahi:
2025-07-02 05:49:18.018 if blo < bhi:
2025-07-02 05:49:18.030 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:18.041 else:
2025-07-02 05:49:18.049 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:18.065 elif blo < bhi:
2025-07-02 05:49:18.076 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:18.087
2025-07-02 05:49:18.098 >       yield from g
2025-07-02 05:49:18.107
2025-07-02 05:49:18.115 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:18.123 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:18.133
2025-07-02 05:49:18.144 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:18.153 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:18.161 alo = 246, ahi = 1101
2025-07-02 05:49:18.172 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:18.184 blo = 246, bhi = 1101
2025-07-02 05:49:18.197
2025-07-02 05:49:18.210 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:18.221 r"""
2025-07-02 05:49:18.231 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:18.239 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:18.247 synch point, and intraline difference marking is done on the
2025-07-02 05:49:18.254 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:18.267
2025-07-02 05:49:18.277 Example:
2025-07-02 05:49:18.285
2025-07-02 05:49:18.291 >>> d = Differ()
2025-07-02 05:49:18.304 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:18.318 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:18.328 >>> print(''.join(results), end="")
2025-07-02 05:49:18.338 - abcDefghiJkl
2025-07-02 05:49:18.367 + abcdefGhijkl
2025-07-02 05:49:18.390 """
2025-07-02 05:49:18.399
2025-07-02 05:49:18.406 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:18.420 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:18.430 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:18.440 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:18.448 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:18.455
2025-07-02 05:49:18.465 # search for the pair that matches best without being identical
2025-07-02 05:49:18.477 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:18.484 # on junk -- unless we have to)
2025-07-02 05:49:18.490 for j in range(blo, bhi):
2025-07-02 05:49:18.496 bj = b[j]
2025-07-02 05:49:18.502 cruncher.set_seq2(bj)
2025-07-02 05:49:18.509 for i in range(alo, ahi):
2025-07-02 05:49:18.515 ai = a[i]
2025-07-02 05:49:18.521 if ai == bj:
2025-07-02 05:49:18.534 if eqi is None:
2025-07-02 05:49:18.542 eqi, eqj = i, j
2025-07-02 05:49:18.550 continue
2025-07-02 05:49:18.561 cruncher.set_seq1(ai)
2025-07-02 05:49:18.571 # computing similarity is expensive, so use the quick
2025-07-02 05:49:18.578 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:18.584 # compares by a factor of 3.
2025-07-02 05:49:18.589 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:18.595 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:18.601 # of the computation is cached by cruncher
2025-07-02 05:49:18.607 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:18.613 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:18.619 cruncher.ratio() > best_ratio:
2025-07-02 05:49:18.627 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:18.638 if best_ratio < cutoff:
2025-07-02 05:49:18.647 # no non-identical "pretty close" pair
2025-07-02 05:49:18.654 if eqi is None:
2025-07-02 05:49:18.660 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:18.668 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:18.675 return
2025-07-02 05:49:18.682 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:18.690 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:18.697 else:
2025-07-02 05:49:18.704 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:18.711 eqi = None
2025-07-02 05:49:18.720
2025-07-02 05:49:18.734 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:18.742 # identical
2025-07-02 05:49:18.749
2025-07-02 05:49:18.760 # pump out diffs from before the synch point
2025-07-02 05:49:18.769 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:18.775
2025-07-02 05:49:18.783 # do intraline marking on the synch pair
2025-07-02 05:49:18.794 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:18.803 if eqi is None:
2025-07-02 05:49:18.810 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:18.815 atags = btags = ""
2025-07-02 05:49:18.821 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:18.828 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:18.833 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:18.839 if tag == 'replace':
2025-07-02 05:49:18.846 atags += '^' * la
2025-07-02 05:49:18.855 btags += '^' * lb
2025-07-02 05:49:18.864 elif tag == 'delete':
2025-07-02 05:49:18.870 atags += '-' * la
2025-07-02 05:49:18.877 elif tag == 'insert':
2025-07-02 05:49:18.883 btags += '+' * lb
2025-07-02 05:49:18.889 elif tag == 'equal':
2025-07-02 05:49:18.895 atags += ' ' * la
2025-07-02 05:49:18.901 btags += ' ' * lb
2025-07-02 05:49:18.907 else:
2025-07-02 05:49:18.913 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:18.919 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:18.925 else:
2025-07-02 05:49:18.931 # the synch pair is identical
2025-07-02 05:49:18.937 yield '  ' + aelt
2025-07-02 05:49:18.943
2025-07-02 05:49:18.948 # pump out diffs from after the synch point
2025-07-02 05:49:18.954 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:18.960
2025-07-02 05:49:18.966 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:18.972 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:18.978
2025-07-02 05:49:18.985 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:18.995 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:19.003 alo = 247, ahi = 1101
2025-07-02 05:49:19.010 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:19.017 blo = 247, bhi = 1101
2025-07-02 05:49:19.026
2025-07-02 05:49:19.035 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:19.044 g = []
2025-07-02 05:49:19.051 if alo < ahi:
2025-07-02 05:49:19.057 if blo < bhi:
2025-07-02 05:49:19.063 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:19.074 else:
2025-07-02 05:49:19.083 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:19.090 elif blo < bhi:
2025-07-02 05:49:19.097 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:19.104
2025-07-02 05:49:19.112 >       yield from g
2025-07-02 05:49:19.119
2025-07-02 05:49:19.126 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:19.134 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:19.146
2025-07-02 05:49:19.156 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:19.164 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:19.172 alo = 247, ahi = 1101
2025-07-02 05:49:19.185 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:19.197 blo = 247, bhi = 1101
2025-07-02 05:49:19.210
2025-07-02 05:49:19.221 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:19.229 r"""
2025-07-02 05:49:19.242 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:19.255 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:19.266 synch point, and intraline difference marking is done on the
2025-07-02 05:49:19.277 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:19.290
2025-07-02 05:49:19.299 Example:
2025-07-02 05:49:19.307
2025-07-02 05:49:19.315 >>> d = Differ()
2025-07-02 05:49:19.322 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:19.330 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:19.340 >>> print(''.join(results), end="")
2025-07-02 05:49:19.350 - abcDefghiJkl
2025-07-02 05:49:19.366 + abcdefGhijkl
2025-07-02 05:49:19.381 """
2025-07-02 05:49:19.388
2025-07-02 05:49:19.396 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:19.402 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:19.409 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:19.416 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:19.423 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:19.430
2025-07-02 05:49:19.443 # search for the pair that matches best without being identical
2025-07-02 05:49:19.455 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:19.467 # on junk -- unless we have to)
2025-07-02 05:49:19.477 for j in range(blo, bhi):
2025-07-02 05:49:19.487 bj = b[j]
2025-07-02 05:49:19.499 cruncher.set_seq2(bj)
2025-07-02 05:49:19.510 for i in range(alo, ahi):
2025-07-02 05:49:19.519 ai = a[i]
2025-07-02 05:49:19.526 if ai == bj:
2025-07-02 05:49:19.539 if eqi is None:
2025-07-02 05:49:19.549 eqi, eqj = i, j
2025-07-02 05:49:19.557 continue
2025-07-02 05:49:19.564 cruncher.set_seq1(ai)
2025-07-02 05:49:19.571 # computing similarity is expensive, so use the quick
2025-07-02 05:49:19.580 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:19.591 # compares by a factor of 3.
2025-07-02 05:49:19.598 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:19.605 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:19.612 # of the computation is cached by cruncher
2025-07-02 05:49:19.619 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:19.627 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:19.639 cruncher.ratio() > best_ratio:
2025-07-02 05:49:19.652 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:19.661 if best_ratio < cutoff:
2025-07-02 05:49:19.671 # no non-identical "pretty close" pair
2025-07-02 05:49:19.678 if eqi is None:
2025-07-02 05:49:19.686 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:19.691 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:19.703 return
2025-07-02 05:49:19.713 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:19.721 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:19.728 else:
2025-07-02 05:49:19.738 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:19.750 eqi = None
2025-07-02 05:49:19.762
2025-07-02 05:49:19.773 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:19.783 # identical
2025-07-02 05:49:19.795
2025-07-02 05:49:19.805 # pump out diffs from before the synch point
2025-07-02 05:49:19.818 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:19.829
2025-07-02 05:49:19.840 # do intraline marking on the synch pair
2025-07-02 05:49:19.851 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:19.861 if eqi is None:
2025-07-02 05:49:19.870 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:19.882 atags = btags = ""
2025-07-02 05:49:19.893 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:19.902 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:19.912 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:19.925 if tag == 'replace':
2025-07-02 05:49:19.935 atags += '^' * la
2025-07-02 05:49:19.944 btags += '^' * lb
2025-07-02 05:49:19.951 elif tag == 'delete':
2025-07-02 05:49:19.959 atags += '-' * la
2025-07-02 05:49:19.971 elif tag == 'insert':
2025-07-02 05:49:19.982 btags += '+' * lb
2025-07-02 05:49:19.992 elif tag == 'equal':
2025-07-02 05:49:19.998 atags += ' ' * la
2025-07-02 05:49:20.006 btags += ' ' * lb
2025-07-02 05:49:20.019 else:
2025-07-02 05:49:20.028 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:20.039 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:20.053 else:
2025-07-02 05:49:20.061 # the synch pair is identical
2025-07-02 05:49:20.067 yield '  ' + aelt
2025-07-02 05:49:20.073
2025-07-02 05:49:20.079 # pump out diffs from after the synch point
2025-07-02 05:49:20.084 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:20.089
2025-07-02 05:49:20.095 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:20.103 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:20.113
2025-07-02 05:49:20.122 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:20.131 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:20.139 alo = 248, ahi = 1101
2025-07-02 05:49:20.146 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:20.152 blo = 248, bhi = 1101
2025-07-02 05:49:20.158
2025-07-02 05:49:20.164 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:20.170 g = []
2025-07-02 05:49:20.180 if alo < ahi:
2025-07-02 05:49:20.190 if blo < bhi:
2025-07-02 05:49:20.201 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:20.211 else:
2025-07-02 05:49:20.218 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:20.224 elif blo < bhi:
2025-07-02 05:49:20.230 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:20.237
2025-07-02 05:49:20.242 >       yield from g
2025-07-02 05:49:20.248
2025-07-02 05:49:20.255 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:20.262 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:20.271
2025-07-02 05:49:20.282 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:20.292 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:20.299 alo = 248, ahi = 1101
2025-07-02 05:49:20.305 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:20.311 blo = 248, bhi = 1101
2025-07-02 05:49:20.316
2025-07-02 05:49:20.322 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:20.329 r"""
2025-07-02 05:49:20.340 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:20.349 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:20.357 synch point, and intraline difference marking is done on the
2025-07-02 05:49:20.363 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:20.369
2025-07-02 05:49:20.374 Example:
2025-07-02 05:49:20.378
2025-07-02 05:49:20.383 >>> d = Differ()
2025-07-02 05:49:20.388 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:20.393 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:20.398 >>> print(''.join(results), end="")
2025-07-02 05:49:20.403 - abcDefghiJkl
2025-07-02 05:49:20.415 + abcdefGhijkl
2025-07-02 05:49:20.428 """
2025-07-02 05:49:20.433
2025-07-02 05:49:20.439 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:20.444 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:20.449 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:20.455 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:20.461 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:20.468
2025-07-02 05:49:20.474 # search for the pair that matches best without being identical
2025-07-02 05:49:20.481 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:20.493 # on junk -- unless we have to)
2025-07-02 05:49:20.501 for j in range(blo, bhi):
2025-07-02 05:49:20.508 bj = b[j]
2025-07-02 05:49:20.515 cruncher.set_seq2(bj)
2025-07-02 05:49:20.525 for i in range(alo, ahi):
2025-07-02 05:49:20.533 ai = a[i]
2025-07-02 05:49:20.544 if ai == bj:
2025-07-02 05:49:20.552 if eqi is None:
2025-07-02 05:49:20.560 eqi, eqj = i, j
2025-07-02 05:49:20.567 continue
2025-07-02 05:49:20.573 cruncher.set_seq1(ai)
2025-07-02 05:49:20.580 # computing similarity is expensive, so use the quick
2025-07-02 05:49:20.586 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:20.593 # compares by a factor of 3.
2025-07-02 05:49:20.599 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:20.607 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:20.614 # of the computation is cached by cruncher
2025-07-02 05:49:20.625 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:20.637 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:20.646 cruncher.ratio() > best_ratio:
2025-07-02 05:49:20.653 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:20.659 if best_ratio < cutoff:
2025-07-02 05:49:20.665 # no non-identical "pretty close" pair
2025-07-02 05:49:20.672 if eqi is None:
2025-07-02 05:49:20.678 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:20.685 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:20.696 return
2025-07-02 05:49:20.706 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:20.712 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:20.718 else:
2025-07-02 05:49:20.725 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:20.732 eqi = None
2025-07-02 05:49:20.739
2025-07-02 05:49:20.745 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:20.752 # identical
2025-07-02 05:49:20.758
2025-07-02 05:49:20.765 # pump out diffs from before the synch point
2025-07-02 05:49:20.772 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:20.778
2025-07-02 05:49:20.784 # do intraline marking on the synch pair
2025-07-02 05:49:20.790 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:20.796 if eqi is None:
2025-07-02 05:49:20.803 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:20.809 atags = btags = ""
2025-07-02 05:49:20.815 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:20.823 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:20.829 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:20.835 if tag == 'replace':
2025-07-02 05:49:20.841 atags += '^' * la
2025-07-02 05:49:20.847 btags += '^' * lb
2025-07-02 05:49:20.853 elif tag == 'delete':
2025-07-02 05:49:20.859 atags += '-' * la
2025-07-02 05:49:20.865 elif tag == 'insert':
2025-07-02 05:49:20.871 btags += '+' * lb
2025-07-02 05:49:20.877 elif tag == 'equal':
2025-07-02 05:49:20.883 atags += ' ' * la
2025-07-02 05:49:20.889 btags += ' ' * lb
2025-07-02 05:49:20.895 else:
2025-07-02 05:49:20.901 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:20.907 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:20.912 else:
2025-07-02 05:49:20.918 # the synch pair is identical
2025-07-02 05:49:20.924 yield '  ' + aelt
2025-07-02 05:49:20.930
2025-07-02 05:49:20.936 # pump out diffs from after the synch point
2025-07-02 05:49:20.942 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:20.951
2025-07-02 05:49:20.962 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:20.970 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:20.977
2025-07-02 05:49:20.983 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:20.989 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:20.993 alo = 249, ahi = 1101
2025-07-02 05:49:20.999 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:21.003 blo = 249, bhi = 1101
2025-07-02 05:49:21.008
2025-07-02 05:49:21.013 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:21.018 g = []
2025-07-02 05:49:21.023 if alo < ahi:
2025-07-02 05:49:21.029 if blo < bhi:
2025-07-02 05:49:21.035 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:21.041 else:
2025-07-02 05:49:21.047 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:21.053 elif blo < bhi:
2025-07-02 05:49:21.059 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:21.065
2025-07-02 05:49:21.070 >       yield from g
2025-07-02 05:49:21.076
2025-07-02 05:49:21.082 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:21.089 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:21.096
2025-07-02 05:49:21.108 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:21.118 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:21.124 alo = 249, ahi = 1101
2025-07-02 05:49:21.131 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:21.137 blo = 249, bhi = 1101
2025-07-02 05:49:21.143
2025-07-02 05:49:21.150 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:21.157 r"""
2025-07-02 05:49:21.165 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:21.172 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:21.179 synch point, and intraline difference marking is done on the
2025-07-02 05:49:21.186 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:21.196
2025-07-02 05:49:21.207 Example:
2025-07-02 05:49:21.214
2025-07-02 05:49:21.219 >>> d = Differ()
2025-07-02 05:49:21.230 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:21.238 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:21.245 >>> print(''.join(results), end="")
2025-07-02 05:49:21.252 - abcDefghiJkl
2025-07-02 05:49:21.265 + abcdefGhijkl
2025-07-02 05:49:21.279 """
2025-07-02 05:49:21.286
2025-07-02 05:49:21.295 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:21.307 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:21.317 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:21.325 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:21.332 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:21.338
2025-07-02 05:49:21.343 # search for the pair that matches best without being identical
2025-07-02 05:49:21.354 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:21.363 # on junk -- unless we have to)
2025-07-02 05:49:21.371 for j in range(blo, bhi):
2025-07-02 05:49:21.382 bj = b[j]
2025-07-02 05:49:21.389 cruncher.set_seq2(bj)
2025-07-02 05:49:21.396 for i in range(alo, ahi):
2025-07-02 05:49:21.403 ai = a[i]
2025-07-02 05:49:21.410 if ai == bj:
2025-07-02 05:49:21.421 if eqi is None:
2025-07-02 05:49:21.430 eqi, eqj = i, j
2025-07-02 05:49:21.441 continue
2025-07-02 05:49:21.453 cruncher.set_seq1(ai)
2025-07-02 05:49:21.463 # computing similarity is expensive, so use the quick
2025-07-02 05:49:21.476 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:21.486 # compares by a factor of 3.
2025-07-02 05:49:21.496 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:21.507 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:21.515 # of the computation is cached by cruncher
2025-07-02 05:49:21.522 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:21.528 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:21.534 cruncher.ratio() > best_ratio:
2025-07-02 05:49:21.540 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:21.546 if best_ratio < cutoff:
2025-07-02 05:49:21.557 # no non-identical "pretty close" pair
2025-07-02 05:49:21.567 if eqi is None:
2025-07-02 05:49:21.574 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:21.581 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:21.588 return
2025-07-02 05:49:21.596 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:21.603 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:21.610 else:
2025-07-02 05:49:21.619 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:21.630 eqi = None
2025-07-02 05:49:21.639
2025-07-02 05:49:21.645 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:21.651 # identical
2025-07-02 05:49:21.656
2025-07-02 05:49:21.661 # pump out diffs from before the synch point
2025-07-02 05:49:21.666 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:21.671
2025-07-02 05:49:21.676 # do intraline marking on the synch pair
2025-07-02 05:49:21.681 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:21.686 if eqi is None:
2025-07-02 05:49:21.691 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:21.697 atags = btags = ""
2025-07-02 05:49:21.703 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:21.709 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:21.715 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:21.723 if tag == 'replace':
2025-07-02 05:49:21.734 atags += '^' * la
2025-07-02 05:49:21.742 btags += '^' * lb
2025-07-02 05:49:21.750 elif tag == 'delete':
2025-07-02 05:49:21.756 atags += '-' * la
2025-07-02 05:49:21.761 elif tag == 'insert':
2025-07-02 05:49:21.766 btags += '+' * lb
2025-07-02 05:49:21.772 elif tag == 'equal':
2025-07-02 05:49:21.778 atags += ' ' * la
2025-07-02 05:49:21.785 btags += ' ' * lb
2025-07-02 05:49:21.791 else:
2025-07-02 05:49:21.798 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:21.804 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:21.811 else:
2025-07-02 05:49:21.818 # the synch pair is identical
2025-07-02 05:49:21.825 yield '  ' + aelt
2025-07-02 05:49:21.831
2025-07-02 05:49:21.838 # pump out diffs from after the synch point
2025-07-02 05:49:21.853 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:21.862
2025-07-02 05:49:21.870 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:21.880 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:21.892
2025-07-02 05:49:21.901 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:21.911 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:21.920 alo = 250, ahi = 1101
2025-07-02 05:49:21.931 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:21.938 blo = 250, bhi = 1101
2025-07-02 05:49:21.945
2025-07-02 05:49:21.952 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:21.962 g = []
2025-07-02 05:49:21.970 if alo < ahi:
2025-07-02 05:49:21.980 if blo < bhi:
2025-07-02 05:49:21.991 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:22.000 else:
2025-07-02 05:49:22.007 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:22.013 elif blo < bhi:
2025-07-02 05:49:22.019 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:22.027
2025-07-02 05:49:22.038 >       yield from g
2025-07-02 05:49:22.048
2025-07-02 05:49:22.056 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:22.061 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:22.066
2025-07-02 05:49:22.071 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:22.076 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:22.081 alo = 250, ahi = 1101
2025-07-02 05:49:22.086 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:22.090 blo = 250, bhi = 1101
2025-07-02 05:49:22.095
2025-07-02 05:49:22.100 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:22.106 r"""
2025-07-02 05:49:22.116 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:22.123 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:22.130 synch point, and intraline difference marking is done on the
2025-07-02 05:49:22.135 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:22.139
2025-07-02 05:49:22.144 Example:
2025-07-02 05:49:22.148
2025-07-02 05:49:22.153 >>> d = Differ()
2025-07-02 05:49:22.158 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:22.162 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:22.167 >>> print(''.join(results), end="")
2025-07-02 05:49:22.172 - abcDefghiJkl
2025-07-02 05:49:22.181 + abcdefGhijkl
2025-07-02 05:49:22.190 """
2025-07-02 05:49:22.195
2025-07-02 05:49:22.199 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:22.204 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:22.209 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:22.214 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:22.218 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:22.223
2025-07-02 05:49:22.228 # search for the pair that matches best without being identical
2025-07-02 05:49:22.233 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:22.238 # on junk -- unless we have to)
2025-07-02 05:49:22.243 for j in range(blo, bhi):
2025-07-02 05:49:22.248 bj = b[j]
2025-07-02 05:49:22.253 cruncher.set_seq2(bj)
2025-07-02 05:49:22.258 for i in range(alo, ahi):
2025-07-02 05:49:22.263 ai = a[i]
2025-07-02 05:49:22.268 if ai == bj:
2025-07-02 05:49:22.273 if eqi is None:
2025-07-02 05:49:22.280 eqi, eqj = i, j
2025-07-02 05:49:22.286 continue
2025-07-02 05:49:22.293 cruncher.set_seq1(ai)
2025-07-02 05:49:22.299 # computing similarity is expensive, so use the quick
2025-07-02 05:49:22.306 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:22.313 # compares by a factor of 3.
2025-07-02 05:49:22.324 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:22.335 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:22.346 # of the computation is cached by cruncher
2025-07-02 05:49:22.356 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:22.364 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:22.371 cruncher.ratio() > best_ratio:
2025-07-02 05:49:22.379 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:22.388 if best_ratio < cutoff:
2025-07-02 05:49:22.396 # no non-identical "pretty close" pair
2025-07-02 05:49:22.405 if eqi is None:
2025-07-02 05:49:22.412 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:22.420 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:22.428 return
2025-07-02 05:49:22.436 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:22.445 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:22.453 else:
2025-07-02 05:49:22.462 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:22.474 eqi = None
2025-07-02 05:49:22.484
2025-07-02 05:49:22.492 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:22.500 # identical
2025-07-02 05:49:22.508
2025-07-02 05:49:22.516 # pump out diffs from before the synch point
2025-07-02 05:49:22.526 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:22.534
2025-07-02 05:49:22.542 # do intraline marking on the synch pair
2025-07-02 05:49:22.550 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:22.558 if eqi is None:
2025-07-02 05:49:22.564 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:22.570 atags = btags = ""
2025-07-02 05:49:22.575 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:22.581 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:22.587 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:22.593 if tag == 'replace':
2025-07-02 05:49:22.599 atags += '^' * la
2025-07-02 05:49:22.606 btags += '^' * lb
2025-07-02 05:49:22.615 elif tag == 'delete':
2025-07-02 05:49:22.624 atags += '-' * la
2025-07-02 05:49:22.631 elif tag == 'insert':
2025-07-02 05:49:22.637 btags += '+' * lb
2025-07-02 05:49:22.644 elif tag == 'equal':
2025-07-02 05:49:22.651 atags += ' ' * la
2025-07-02 05:49:22.662 btags += ' ' * lb
2025-07-02 05:49:22.672 else:
2025-07-02 05:49:22.680 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:22.688 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:22.695 else:
2025-07-02 05:49:22.708 # the synch pair is identical
2025-07-02 05:49:22.716 yield '  ' + aelt
2025-07-02 05:49:22.724
2025-07-02 05:49:22.730 # pump out diffs from after the synch point
2025-07-02 05:49:22.738 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:22.750
2025-07-02 05:49:22.761 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:22.775 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:22.784
2025-07-02 05:49:22.792 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:22.802 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:22.809 alo = 251, ahi = 1101
2025-07-02 05:49:22.820 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:22.831 blo = 251, bhi = 1101
2025-07-02 05:49:22.842
2025-07-02 05:49:22.852 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:22.858 g = []
2025-07-02 05:49:22.865 if alo < ahi:
2025-07-02 05:49:22.872 if blo < bhi:
2025-07-02 05:49:22.884 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:22.891 else:
2025-07-02 05:49:22.897 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:22.902 elif blo < bhi:
2025-07-02 05:49:22.907 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:22.920
2025-07-02 05:49:22.932 >       yield from g
2025-07-02 05:49:22.941
2025-07-02 05:49:22.949 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:22.956 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:22.969
2025-07-02 05:49:22.983 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:22.993 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:23.006 alo = 251, ahi = 1101
2025-07-02 05:49:23.017 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:23.025 blo = 251, bhi = 1101
2025-07-02 05:49:23.032
2025-07-02 05:49:23.038 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:23.048 r"""
2025-07-02 05:49:23.059 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:23.068 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:23.074 synch point, and intraline difference marking is done on the
2025-07-02 05:49:23.084 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:23.093
2025-07-02 05:49:23.105 Example:
2025-07-02 05:49:23.115
2025-07-02 05:49:23.127 >>> d = Differ()
2025-07-02 05:49:23.136 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:23.144 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:23.155 >>> print(''.join(results), end="")
2025-07-02 05:49:23.163 - abcDefghiJkl
2025-07-02 05:49:23.180 + abcdefGhijkl
2025-07-02 05:49:23.198 """
2025-07-02 05:49:23.211
2025-07-02 05:49:23.219 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:23.227 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:23.238 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:23.246 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:23.253 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:23.258
2025-07-02 05:49:23.263 # search for the pair that matches best without being identical
2025-07-02 05:49:23.268 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:23.273 # on junk -- unless we have to)
2025-07-02 05:49:23.278 for j in range(blo, bhi):
2025-07-02 05:49:23.283 bj = b[j]
2025-07-02 05:49:23.288 cruncher.set_seq2(bj)
2025-07-02 05:49:23.294 for i in range(alo, ahi):
2025-07-02 05:49:23.300 ai = a[i]
2025-07-02 05:49:23.305 if ai == bj:
2025-07-02 05:49:23.311 if eqi is None:
2025-07-02 05:49:23.318 eqi, eqj = i, j
2025-07-02 05:49:23.329 continue
2025-07-02 05:49:23.337 cruncher.set_seq1(ai)
2025-07-02 05:49:23.345 # computing similarity is expensive, so use the quick
2025-07-02 05:49:23.352 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:23.358 # compares by a factor of 3.
2025-07-02 05:49:23.365 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:23.371 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:23.376 # of the computation is cached by cruncher
2025-07-02 05:49:23.381 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:23.385 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:23.391 cruncher.ratio() > best_ratio:
2025-07-02 05:49:23.396 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:23.402 if best_ratio < cutoff:
2025-07-02 05:49:23.412 # no non-identical "pretty close" pair
2025-07-02 05:49:23.420 if eqi is None:
2025-07-02 05:49:23.428 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:23.437 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:23.446 return
2025-07-02 05:49:23.459 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:23.468 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:23.478 else:
2025-07-02 05:49:23.485 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:23.492 eqi = None
2025-07-02 05:49:23.499
2025-07-02 05:49:23.508 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:23.522 # identical
2025-07-02 05:49:23.530
2025-07-02 05:49:23.543 # pump out diffs from before the synch point
2025-07-02 05:49:23.555 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:23.565
2025-07-02 05:49:23.574 # do intraline marking on the synch pair
2025-07-02 05:49:23.581 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:23.587 if eqi is None:
2025-07-02 05:49:23.593 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:23.599 atags = btags = ""
2025-07-02 05:49:23.605 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:23.612 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:23.618 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:23.625 if tag == 'replace':
2025-07-02 05:49:23.631 atags += '^' * la
2025-07-02 05:49:23.638 btags += '^' * lb
2025-07-02 05:49:23.643 elif tag == 'delete':
2025-07-02 05:49:23.650 atags += '-' * la
2025-07-02 05:49:23.657 elif tag == 'insert':
2025-07-02 05:49:23.664 btags += '+' * lb
2025-07-02 05:49:23.671 elif tag == 'equal':
2025-07-02 05:49:23.679 atags += ' ' * la
2025-07-02 05:49:23.692 btags += ' ' * lb
2025-07-02 05:49:23.701 else:
2025-07-02 05:49:23.710 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:23.723 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:23.731 else:
2025-07-02 05:49:23.739 # the synch pair is identical
2025-07-02 05:49:23.747 yield '  ' + aelt
2025-07-02 05:49:23.758
2025-07-02 05:49:23.767 # pump out diffs from after the synch point
2025-07-02 05:49:23.774 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:23.787
2025-07-02 05:49:23.799 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:23.809 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:23.817
2025-07-02 05:49:23.829 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:23.837 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:23.843 alo = 252, ahi = 1101
2025-07-02 05:49:23.850 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:23.861 blo = 252, bhi = 1101
2025-07-02 05:49:23.873
2025-07-02 05:49:23.882 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:23.891 g = []
2025-07-02 05:49:23.900 if alo < ahi:
2025-07-02 05:49:23.912 if blo < bhi:
2025-07-02 05:49:23.920 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:23.926 else:
2025-07-02 05:49:23.934 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:23.945 elif blo < bhi:
2025-07-02 05:49:23.952 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:23.962
2025-07-02 05:49:23.973 >       yield from g
2025-07-02 05:49:23.983
2025-07-02 05:49:23.990 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:23.998 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:24.007
2025-07-02 05:49:24.019 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:24.029 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:24.037 alo = 252, ahi = 1101
2025-07-02 05:49:24.046 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:24.058 blo = 252, bhi = 1101
2025-07-02 05:49:24.068
2025-07-02 05:49:24.078 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:24.087 r"""
2025-07-02 05:49:24.094 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:24.104 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:24.118 synch point, and intraline difference marking is done on the
2025-07-02 05:49:24.127 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:24.135
2025-07-02 05:49:24.144 Example:
2025-07-02 05:49:24.152
2025-07-02 05:49:24.158 >>> d = Differ()
2025-07-02 05:49:24.165 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:24.172 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:24.178 >>> print(''.join(results), end="")
2025-07-02 05:49:24.189 - abcDefghiJkl
2025-07-02 05:49:24.208 + abcdefGhijkl
2025-07-02 05:49:24.220 """
2025-07-02 05:49:24.225
2025-07-02 05:49:24.237 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:24.246 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:24.253 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:24.260 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:24.267 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:24.275
2025-07-02 05:49:24.286 # search for the pair that matches best without being identical
2025-07-02 05:49:24.294 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:24.302 # on junk -- unless we have to)
2025-07-02 05:49:24.313 for j in range(blo, bhi):
2025-07-02 05:49:24.322 bj = b[j]
2025-07-02 05:49:24.335 cruncher.set_seq2(bj)
2025-07-02 05:49:24.346 for i in range(alo, ahi):
2025-07-02 05:49:24.355 ai = a[i]
2025-07-02 05:49:24.363 if ai == bj:
2025-07-02 05:49:24.369 if eqi is None:
2025-07-02 05:49:24.376 eqi, eqj = i, j
2025-07-02 05:49:24.382 continue
2025-07-02 05:49:24.387 cruncher.set_seq1(ai)
2025-07-02 05:49:24.398 # computing similarity is expensive, so use the quick
2025-07-02 05:49:24.410 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:24.423 # compares by a factor of 3.
2025-07-02 05:49:24.432 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:24.441 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:24.448 # of the computation is cached by cruncher
2025-07-02 05:49:24.455 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:24.462 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:24.468 cruncher.ratio() > best_ratio:
2025-07-02 05:49:24.474 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:24.481 if best_ratio < cutoff:
2025-07-02 05:49:24.495 # no non-identical "pretty close" pair
2025-07-02 05:49:24.506 if eqi is None:
2025-07-02 05:49:24.515 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:24.523 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:24.530 return
2025-07-02 05:49:24.539 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:24.551 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:24.559 else:
2025-07-02 05:49:24.566 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:24.578 eqi = None
2025-07-02 05:49:24.586
2025-07-02 05:49:24.594 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:24.622 # identical
2025-07-02 05:49:24.638
2025-07-02 05:49:24.649 # pump out diffs from before the synch point
2025-07-02 05:49:24.659 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:24.669
2025-07-02 05:49:24.676 # do intraline marking on the synch pair
2025-07-02 05:49:24.686 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:24.695 if eqi is None:
2025-07-02 05:49:24.704 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:24.712 atags = btags = ""
2025-07-02 05:49:24.720 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:24.734 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:24.748 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:24.759 if tag == 'replace':
2025-07-02 05:49:24.768 atags += '^' * la
2025-07-02 05:49:24.776 btags += '^' * lb
2025-07-02 05:49:24.789 elif tag == 'delete':
2025-07-02 05:49:24.798 atags += '-' * la
2025-07-02 05:49:24.807 elif tag == 'insert':
2025-07-02 05:49:24.815 btags += '+' * lb
2025-07-02 05:49:24.828 elif tag == 'equal':
2025-07-02 05:49:24.840 atags += ' ' * la
2025-07-02 05:49:24.854 btags += ' ' * lb
2025-07-02 05:49:24.864 else:
2025-07-02 05:49:24.873 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:24.880 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:24.888 else:
2025-07-02 05:49:24.896 # the synch pair is identical
2025-07-02 05:49:24.903 yield '  ' + aelt
2025-07-02 05:49:24.909
2025-07-02 05:49:24.918 # pump out diffs from after the synch point
2025-07-02 05:49:24.927 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:24.935
2025-07-02 05:49:24.942 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:24.950 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:24.956
2025-07-02 05:49:24.966 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:24.977 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:24.985 alo = 253, ahi = 1101
2025-07-02 05:49:24.996 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:25.002 blo = 253, bhi = 1101
2025-07-02 05:49:25.009
2025-07-02 05:49:25.015 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:25.023 g = []
2025-07-02 05:49:25.029 if alo < ahi:
2025-07-02 05:49:25.042 if blo < bhi:
2025-07-02 05:49:25.056 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:25.068 else:
2025-07-02 05:49:25.083 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:25.095 elif blo < bhi:
2025-07-02 05:49:25.104 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:25.113
2025-07-02 05:49:25.121 >       yield from g
2025-07-02 05:49:25.127
2025-07-02 05:49:25.135 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:25.147 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:25.160
2025-07-02 05:49:25.173 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:25.186 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:25.196 alo = 253, ahi = 1101
2025-07-02 05:49:25.210 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:25.220 blo = 253, bhi = 1101
2025-07-02 05:49:25.229
2025-07-02 05:49:25.236 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:25.242 r"""
2025-07-02 05:49:25.250 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:25.280 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:25.289 synch point, and intraline difference marking is done on the
2025-07-02 05:49:25.301 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:25.312
2025-07-02 05:49:25.324 Example:
2025-07-02 05:49:25.335
2025-07-02 05:49:25.347 >>> d = Differ()
2025-07-02 05:49:25.356 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:25.363 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:25.369 >>> print(''.join(results), end="")
2025-07-02 05:49:25.375 - abcDefghiJkl
2025-07-02 05:49:25.386 + abcdefGhijkl
2025-07-02 05:49:25.396 """
2025-07-02 05:49:25.401
2025-07-02 05:49:25.407 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:25.414 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:25.422 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:25.429 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:25.437 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:25.444
2025-07-02 05:49:25.452 # search for the pair that matches best without being identical
2025-07-02 05:49:25.459 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:25.465 # on junk -- unless we have to)
2025-07-02 05:49:25.472 for j in range(blo, bhi):
2025-07-02 05:49:25.479 bj = b[j]
2025-07-02 05:49:25.486 cruncher.set_seq2(bj)
2025-07-02 05:49:25.493 for i in range(alo, ahi):
2025-07-02 05:49:25.503 ai = a[i]
2025-07-02 05:49:25.511 if ai == bj:
2025-07-02 05:49:25.520 if eqi is None:
2025-07-02 05:49:25.528 eqi, eqj = i, j
2025-07-02 05:49:25.535 continue
2025-07-02 05:49:25.542 cruncher.set_seq1(ai)
2025-07-02 05:49:25.549 # computing similarity is expensive, so use the quick
2025-07-02 05:49:25.556 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:25.563 # compares by a factor of 3.
2025-07-02 05:49:25.571 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:25.578 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:25.585 # of the computation is cached by cruncher
2025-07-02 05:49:25.592 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:25.598 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:25.606 cruncher.ratio() > best_ratio:
2025-07-02 05:49:25.613 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:25.619 if best_ratio < cutoff:
2025-07-02 05:49:25.626 # no non-identical "pretty close" pair
2025-07-02 05:49:25.632 if eqi is None:
2025-07-02 05:49:25.639 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:25.646 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:25.652 return
2025-07-02 05:49:25.658 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:25.665 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:25.672 else:
2025-07-02 05:49:25.680 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:25.688 eqi = None
2025-07-02 05:49:25.695
2025-07-02 05:49:25.702 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:25.711 # identical
2025-07-02 05:49:25.724
2025-07-02 05:49:25.734 # pump out diffs from before the synch point
2025-07-02 05:49:25.743 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:25.752
2025-07-02 05:49:25.763 # do intraline marking on the synch pair
2025-07-02 05:49:25.772 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:25.780 if eqi is None:
2025-07-02 05:49:25.787 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:25.793 atags = btags = ""
2025-07-02 05:49:25.800 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:25.807 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:25.814 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:25.821 if tag == 'replace':
2025-07-02 05:49:25.828 atags += '^' * la
2025-07-02 05:49:25.841 btags += '^' * lb
2025-07-02 05:49:25.852 elif tag == 'delete':
2025-07-02 05:49:25.862 atags += '-' * la
2025-07-02 05:49:25.870 elif tag == 'insert':
2025-07-02 05:49:25.881 btags += '+' * lb
2025-07-02 05:49:25.891 elif tag == 'equal':
2025-07-02 05:49:25.900 atags += ' ' * la
2025-07-02 05:49:25.908 btags += ' ' * lb
2025-07-02 05:49:25.915 else:
2025-07-02 05:49:25.921 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:25.930 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:25.943 else:
2025-07-02 05:49:25.952 # the synch pair is identical
2025-07-02 05:49:25.967 yield '  ' + aelt
2025-07-02 05:49:25.977
2025-07-02 05:49:25.985 # pump out diffs from after the synch point
2025-07-02 05:49:25.992 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:25.999
2025-07-02 05:49:26.005 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:26.011 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:26.017
2025-07-02 05:49:26.023 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:26.030 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:26.037 alo = 254, ahi = 1101
2025-07-02 05:49:26.043 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:26.049 blo = 254, bhi = 1101
2025-07-02 05:49:26.055
2025-07-02 05:49:26.064 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:26.074 g = []
2025-07-02 05:49:26.081 if alo < ahi:
2025-07-02 05:49:26.086 if blo < bhi:
2025-07-02 05:49:26.094 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:26.102 else:
2025-07-02 05:49:26.110 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:26.116 elif blo < bhi:
2025-07-02 05:49:26.123 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:26.129
2025-07-02 05:49:26.142 >       yield from g
2025-07-02 05:49:26.155
2025-07-02 05:49:26.166 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:26.174 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:26.181
2025-07-02 05:49:26.192 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:26.205 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:26.215 alo = 254, ahi = 1101
2025-07-02 05:49:26.223 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:26.228 blo = 254, bhi = 1101
2025-07-02 05:49:26.234
2025-07-02 05:49:26.243 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:26.254 r"""
2025-07-02 05:49:26.263 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:26.270 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:26.276 synch point, and intraline difference marking is done on the
2025-07-02 05:49:26.282 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:26.292
2025-07-02 05:49:26.302 Example:
2025-07-02 05:49:26.308
2025-07-02 05:49:26.315 >>> d = Differ()
2025-07-02 05:49:26.320 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:26.325 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:26.338 >>> print(''.join(results), end="")
2025-07-02 05:49:26.350 - abcDefghiJkl
2025-07-02 05:49:26.371 + abcdefGhijkl
2025-07-02 05:49:26.387 """
2025-07-02 05:49:26.400
2025-07-02 05:49:26.411 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:26.420 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:26.427 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:26.434 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:26.445 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:26.451
2025-07-02 05:49:26.458 # search for the pair that matches best without being identical
2025-07-02 05:49:26.467 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:26.478 # on junk -- unless we have to)
2025-07-02 05:49:26.486 for j in range(blo, bhi):
2025-07-02 05:49:26.497 bj = b[j]
2025-07-02 05:49:26.507 cruncher.set_seq2(bj)
2025-07-02 05:49:26.513 for i in range(alo, ahi):
2025-07-02 05:49:26.518 ai = a[i]
2025-07-02 05:49:26.524 if ai == bj:
2025-07-02 05:49:26.531 if eqi is None:
2025-07-02 05:49:26.538 eqi, eqj = i, j
2025-07-02 05:49:26.549 continue
2025-07-02 05:49:26.561 cruncher.set_seq1(ai)
2025-07-02 05:49:26.570 # computing similarity is expensive, so use the quick
2025-07-02 05:49:26.577 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:26.584 # compares by a factor of 3.
2025-07-02 05:49:26.590 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:26.595 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:26.600 # of the computation is cached by cruncher
2025-07-02 05:49:26.606 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:26.611 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:26.623 cruncher.ratio() > best_ratio:
2025-07-02 05:49:26.635 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:26.643 if best_ratio < cutoff:
2025-07-02 05:49:26.650 # no non-identical "pretty close" pair
2025-07-02 05:49:26.659 if eqi is None:
2025-07-02 05:49:26.670 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:26.679 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:26.687 return
2025-07-02 05:49:26.696 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:26.704 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:26.711 else:
2025-07-02 05:49:26.718 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:26.730 eqi = None
2025-07-02 05:49:26.738
2025-07-02 05:49:26.748 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:26.756 # identical
2025-07-02 05:49:26.766
2025-07-02 05:49:26.778 # pump out diffs from before the synch point
2025-07-02 05:49:26.790 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:26.798
2025-07-02 05:49:26.805 # do intraline marking on the synch pair
2025-07-02 05:49:26.812 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:26.818 if eqi is None:
2025-07-02 05:49:26.825 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:26.831 atags = btags = ""
2025-07-02 05:49:26.837 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:26.850 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:26.863 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:26.877 if tag == 'replace':
2025-07-02 05:49:26.891 atags += '^' * la
2025-07-02 05:49:26.905 btags += '^' * lb
2025-07-02 05:49:26.915 elif tag == 'delete':
2025-07-02 05:49:26.925 atags += '-' * la
2025-07-02 05:49:26.932 elif tag == 'insert':
2025-07-02 05:49:26.939 btags += '+' * lb
2025-07-02 05:49:26.947 elif tag == 'equal':
2025-07-02 05:49:26.954 atags += ' ' * la
2025-07-02 05:49:26.963 btags += ' ' * lb
2025-07-02 05:49:26.974 else:
2025-07-02 05:49:26.984 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:26.997 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:27.010 else:
2025-07-02 05:49:27.021 # the synch pair is identical
2025-07-02 05:49:27.029 yield '  ' + aelt
2025-07-02 05:49:27.035
2025-07-02 05:49:27.042 # pump out diffs from after the synch point
2025-07-02 05:49:27.050 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:27.061
2025-07-02 05:49:27.072 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:27.081 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:27.088
2025-07-02 05:49:27.095 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:27.107 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:27.117 alo = 255, ahi = 1101
2025-07-02 05:49:27.129 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:27.140 blo = 255, bhi = 1101
2025-07-02 05:49:27.153
2025-07-02 05:49:27.165 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:27.174 g = []
2025-07-02 05:49:27.183 if alo < ahi:
2025-07-02 05:49:27.194 if blo < bhi:
2025-07-02 05:49:27.204 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:27.212 else:
2025-07-02 05:49:27.221 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:27.232 elif blo < bhi:
2025-07-02 05:49:27.240 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:27.247
2025-07-02 05:49:27.255 >       yield from g
2025-07-02 05:49:27.266
2025-07-02 05:49:27.274 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:27.284 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:27.290
2025-07-02 05:49:27.303 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:27.314 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:27.324 alo = 255, ahi = 1101
2025-07-02 05:49:27.337 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:27.345 blo = 255, bhi = 1101
2025-07-02 05:49:27.355
2025-07-02 05:49:27.366 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:27.377 r"""
2025-07-02 05:49:27.388 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:27.397 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:27.406 synch point, and intraline difference marking is done on the
2025-07-02 05:49:27.414 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:27.440
2025-07-02 05:49:27.453 Example:
2025-07-02 05:49:27.461
2025-07-02 05:49:27.473 >>> d = Differ()
2025-07-02 05:49:27.486 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:27.497 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:27.506 >>> print(''.join(results), end="")
2025-07-02 05:49:27.515 - abcDefghiJkl
2025-07-02 05:49:27.538 + abcdefGhijkl
2025-07-02 05:49:27.555 """
2025-07-02 05:49:27.563
2025-07-02 05:49:27.571 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:27.585 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:27.592 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:27.600 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:27.608 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:27.615
2025-07-02 05:49:27.622 # search for the pair that matches best without being identical
2025-07-02 05:49:27.631 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:27.642 # on junk -- unless we have to)
2025-07-02 05:49:27.655 for j in range(blo, bhi):
2025-07-02 05:49:27.666 bj = b[j]
2025-07-02 05:49:27.677 cruncher.set_seq2(bj)
2025-07-02 05:49:27.688 for i in range(alo, ahi):
2025-07-02 05:49:27.700 ai = a[i]
2025-07-02 05:49:27.712 if ai == bj:
2025-07-02 05:49:27.722 if eqi is None:
2025-07-02 05:49:27.730 eqi, eqj = i, j
2025-07-02 05:49:27.740 continue
2025-07-02 05:49:27.753 cruncher.set_seq1(ai)
2025-07-02 05:49:27.762 # computing similarity is expensive, so use the quick
2025-07-02 05:49:27.773 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:27.784 # compares by a factor of 3.
2025-07-02 05:49:27.793 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:27.801 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:27.808 # of the computation is cached by cruncher
2025-07-02 05:49:27.815 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:27.822 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:27.833 cruncher.ratio() > best_ratio:
2025-07-02 05:49:27.843 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:27.851 if best_ratio < cutoff:
2025-07-02 05:49:27.861 # no non-identical "pretty close" pair
2025-07-02 05:49:27.870 if eqi is None:
2025-07-02 05:49:27.882 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:27.892 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:27.905 return
2025-07-02 05:49:27.917 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:27.928 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:27.937 else:
2025-07-02 05:49:27.949 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:27.959 eqi = None
2025-07-02 05:49:27.971
2025-07-02 05:49:27.981 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:27.994 # identical
2025-07-02 05:49:28.007
2025-07-02 05:49:28.022 # pump out diffs from before the synch point
2025-07-02 05:49:28.031 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:28.038
2025-07-02 05:49:28.045 # do intraline marking on the synch pair
2025-07-02 05:49:28.052 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:28.058 if eqi is None:
2025-07-02 05:49:28.063 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:28.069 atags = btags = ""
2025-07-02 05:49:28.074 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:28.083 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:28.091 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:28.098 if tag == 'replace':
2025-07-02 05:49:28.104 atags += '^' * la
2025-07-02 05:49:28.110 btags += '^' * lb
2025-07-02 05:49:28.116 elif tag == 'delete':
2025-07-02 05:49:28.128 atags += '-' * la
2025-07-02 05:49:28.137 elif tag == 'insert':
2025-07-02 05:49:28.147 btags += '+' * lb
2025-07-02 05:49:28.160 elif tag == 'equal':
2025-07-02 05:49:28.171 atags += ' ' * la
2025-07-02 05:49:28.179 btags += ' ' * lb
2025-07-02 05:49:28.185 else:
2025-07-02 05:49:28.192 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:28.198 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:28.204 else:
2025-07-02 05:49:28.210 # the synch pair is identical
2025-07-02 05:49:28.219 yield '  ' + aelt
2025-07-02 05:49:28.230
2025-07-02 05:49:28.238 # pump out diffs from after the synch point
2025-07-02 05:49:28.246 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:28.257
2025-07-02 05:49:28.265 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:28.272 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:28.278
2025-07-02 05:49:28.292 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:28.305 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:28.314 alo = 256, ahi = 1101
2025-07-02 05:49:28.325 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:28.339 blo = 256, bhi = 1101
2025-07-02 05:49:28.348
2025-07-02 05:49:28.359 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:28.371 g = []
2025-07-02 05:49:28.380 if alo < ahi:
2025-07-02 05:49:28.388 if blo < bhi:
2025-07-02 05:49:28.395 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:28.401 else:
2025-07-02 05:49:28.407 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:28.419 elif blo < bhi:
2025-07-02 05:49:28.429 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:28.437
2025-07-02 05:49:28.445 >       yield from g
2025-07-02 05:49:28.457
2025-07-02 05:49:28.465 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:28.472 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:28.480
2025-07-02 05:49:28.487 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:28.497 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:28.511 alo = 256, ahi = 1101
2025-07-02 05:49:28.526 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:28.538 blo = 256, bhi = 1101
2025-07-02 05:49:28.551
2025-07-02 05:49:28.560 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:28.567 r"""
2025-07-02 05:49:28.577 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:28.590 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:28.601 synch point, and intraline difference marking is done on the
2025-07-02 05:49:28.616 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:28.627
2025-07-02 05:49:28.635 Example:
2025-07-02 05:49:28.642
2025-07-02 05:49:28.654 >>> d = Differ()
2025-07-02 05:49:28.664 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:28.675 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:28.686 >>> print(''.join(results), end="")
2025-07-02 05:49:28.695 - abcDefghiJkl
2025-07-02 05:49:28.709 + abcdefGhijkl
2025-07-02 05:49:28.721 """
2025-07-02 05:49:28.727
2025-07-02 05:49:28.733 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:28.740 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:28.747 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:28.754 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:28.764 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:28.776
2025-07-02 05:49:28.785 # search for the pair that matches best without being identical
2025-07-02 05:49:28.792 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:28.797 # on junk -- unless we have to)
2025-07-02 05:49:28.802 for j in range(blo, bhi):
2025-07-02 05:49:28.807 bj = b[j]
2025-07-02 05:49:28.812 cruncher.set_seq2(bj)
2025-07-02 05:49:28.817 for i in range(alo, ahi):
2025-07-02 05:49:28.829 ai = a[i]
2025-07-02 05:49:28.840 if ai == bj:
2025-07-02 05:49:28.849 if eqi is None:
2025-07-02 05:49:28.856 eqi, eqj = i, j
2025-07-02 05:49:28.862 continue
2025-07-02 05:49:28.868 cruncher.set_seq1(ai)
2025-07-02 05:49:28.873 # computing similarity is expensive, so use the quick
2025-07-02 05:49:28.878 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:28.883 # compares by a factor of 3.
2025-07-02 05:49:28.890 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:28.899 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:28.908 # of the computation is cached by cruncher
2025-07-02 05:49:28.915 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:28.923 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:28.930 cruncher.ratio() > best_ratio:
2025-07-02 05:49:28.939 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:28.946 if best_ratio < cutoff:
2025-07-02 05:49:28.954 # no non-identical "pretty close" pair
2025-07-02 05:49:28.960 if eqi is None:
2025-07-02 05:49:28.966 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:28.973 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:28.981 return
2025-07-02 05:49:28.988 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:28.996 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:29.002 else:
2025-07-02 05:49:29.008 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:29.022 eqi = None
2025-07-02 05:49:29.030
2025-07-02 05:49:29.038 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:29.046 # identical
2025-07-02 05:49:29.057
2025-07-02 05:49:29.070 # pump out diffs from before the synch point
2025-07-02 05:49:29.083 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:29.093
2025-07-02 05:49:29.106 # do intraline marking on the synch pair
2025-07-02 05:49:29.117 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:29.130 if eqi is None:
2025-07-02 05:49:29.141 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:29.152 atags = btags = ""
2025-07-02 05:49:29.164 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:29.173 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:29.181 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:29.189 if tag == 'replace':
2025-07-02 05:49:29.197 atags += '^' * la
2025-07-02 05:49:29.209 btags += '^' * lb
2025-07-02 05:49:29.224 elif tag == 'delete':
2025-07-02 05:49:29.235 atags += '-' * la
2025-07-02 05:49:29.242 elif tag == 'insert':
2025-07-02 05:49:29.247 btags += '+' * lb
2025-07-02 05:49:29.253 elif tag == 'equal':
2025-07-02 05:49:29.258 atags += ' ' * la
2025-07-02 05:49:29.265 btags += ' ' * lb
2025-07-02 05:49:29.277 else:
2025-07-02 05:49:29.289 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:29.297 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:29.305 else:
2025-07-02 05:49:29.315 # the synch pair is identical
2025-07-02 05:49:29.322 yield '  ' + aelt
2025-07-02 05:49:29.327
2025-07-02 05:49:29.334 # pump out diffs from after the synch point
2025-07-02 05:49:29.341 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:29.348
2025-07-02 05:49:29.356 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:29.364 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:29.371
2025-07-02 05:49:29.379 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:29.387 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:29.394 alo = 257, ahi = 1101
2025-07-02 05:49:29.403 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:29.411 blo = 257, bhi = 1101
2025-07-02 05:49:29.422
2025-07-02 05:49:29.436 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:29.447 g = []
2025-07-02 05:49:29.456 if alo < ahi:
2025-07-02 05:49:29.462 if blo < bhi:
2025-07-02 05:49:29.468 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:29.473 else:
2025-07-02 05:49:29.479 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:29.485 elif blo < bhi:
2025-07-02 05:49:29.490 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:29.501
2025-07-02 05:49:29.512 >       yield from g
2025-07-02 05:49:29.523
2025-07-02 05:49:29.532 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:29.540 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:29.547
2025-07-02 05:49:29.554 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:29.569 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:29.581 alo = 257, ahi = 1101
2025-07-02 05:49:29.589 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:29.596 blo = 257, bhi = 1101
2025-07-02 05:49:29.602
2025-07-02 05:49:29.609 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:29.614 r"""
2025-07-02 05:49:29.619 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:29.625 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:29.636 synch point, and intraline difference marking is done on the
2025-07-02 05:49:29.645 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:29.652
2025-07-02 05:49:29.659 Example:
2025-07-02 05:49:29.672
2025-07-02 05:49:29.684 >>> d = Differ()
2025-07-02 05:49:29.698 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:29.711 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:29.722 >>> print(''.join(results), end="")
2025-07-02 05:49:29.731 - abcDefghiJkl
2025-07-02 05:49:29.747 + abcdefGhijkl
2025-07-02 05:49:29.766 """
2025-07-02 05:49:29.775
2025-07-02 05:49:29.783 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:29.795 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:29.804 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:29.812 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:29.819 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:29.827
2025-07-02 05:49:29.838 # search for the pair that matches best without being identical
2025-07-02 05:49:29.847 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:29.854 # on junk -- unless we have to)
2025-07-02 05:49:29.863 for j in range(blo, bhi):
2025-07-02 05:49:29.871 bj = b[j]
2025-07-02 05:49:29.878 cruncher.set_seq2(bj)
2025-07-02 05:49:29.889 for i in range(alo, ahi):
2025-07-02 05:49:29.900 ai = a[i]
2025-07-02 05:49:29.908 if ai == bj:
2025-07-02 05:49:29.916 if eqi is None:
2025-07-02 05:49:29.929 eqi, eqj = i, j
2025-07-02 05:49:29.940 continue
2025-07-02 05:49:29.950 cruncher.set_seq1(ai)
2025-07-02 05:49:29.959 # computing similarity is expensive, so use the quick
2025-07-02 05:49:29.968 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:29.974 # compares by a factor of 3.
2025-07-02 05:49:29.980 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:29.986 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:29.997 # of the computation is cached by cruncher
2025-07-02 05:49:30.006 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:30.019 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:30.028 cruncher.ratio() > best_ratio:
2025-07-02 05:49:30.042 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:30.051 if best_ratio < cutoff:
2025-07-02 05:49:30.058 # no non-identical "pretty close" pair
2025-07-02 05:49:30.065 if eqi is None:
2025-07-02 05:49:30.073 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:30.078 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:30.083 return
2025-07-02 05:49:30.088 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:30.094 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:30.100 else:
2025-07-02 05:49:30.105 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:30.111 eqi = None
2025-07-02 05:49:30.117
2025-07-02 05:49:30.122 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:30.133 # identical
2025-07-02 05:49:30.143
2025-07-02 05:49:30.149 # pump out diffs from before the synch point
2025-07-02 05:49:30.154 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:30.159
2025-07-02 05:49:30.165 # do intraline marking on the synch pair
2025-07-02 05:49:30.170 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:30.176 if eqi is None:
2025-07-02 05:49:30.183 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:30.191 atags = btags = ""
2025-07-02 05:49:30.203 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:30.212 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:30.218 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:30.226 if tag == 'replace':
2025-07-02 05:49:30.236 atags += '^' * la
2025-07-02 05:49:30.246 btags += '^' * lb
2025-07-02 05:49:30.252 elif tag == 'delete':
2025-07-02 05:49:30.260 atags += '-' * la
2025-07-02 05:49:30.268 elif tag == 'insert':
2025-07-02 05:49:30.274 btags += '+' * lb
2025-07-02 05:49:30.281 elif tag == 'equal':
2025-07-02 05:49:30.287 atags += ' ' * la
2025-07-02 05:49:30.294 btags += ' ' * lb
2025-07-02 05:49:30.305 else:
2025-07-02 05:49:30.315 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:30.323 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:30.330 else:
2025-07-02 05:49:30.337 # the synch pair is identical
2025-07-02 05:49:30.342 yield '  ' + aelt
2025-07-02 05:49:30.348
2025-07-02 05:49:30.354 # pump out diffs from after the synch point
2025-07-02 05:49:30.367 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:30.377
2025-07-02 05:49:30.386 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:30.394 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:30.400
2025-07-02 05:49:30.406 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:30.412 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:30.418 alo = 258, ahi = 1101
2025-07-02 05:49:30.429 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:30.439 blo = 258, bhi = 1101
2025-07-02 05:49:30.448
2025-07-02 05:49:30.455 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:30.461 g = []
2025-07-02 05:49:30.467 if alo < ahi:
2025-07-02 05:49:30.474 if blo < bhi:
2025-07-02 05:49:30.480 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:30.486 else:
2025-07-02 05:49:30.493 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:30.499 elif blo < bhi:
2025-07-02 05:49:30.507 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:30.520
2025-07-02 05:49:30.529 >       yield from g
2025-07-02 05:49:30.539
2025-07-02 05:49:30.552 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:30.562 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:30.574
2025-07-02 05:49:30.585 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:30.594 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:30.602 alo = 258, ahi = 1101
2025-07-02 05:49:30.611 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:30.617 blo = 258, bhi = 1101
2025-07-02 05:49:30.622
2025-07-02 05:49:30.630 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:30.637 r"""
2025-07-02 05:49:30.644 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:30.650 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:30.656 synch point, and intraline difference marking is done on the
2025-07-02 05:49:30.662 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:30.669
2025-07-02 05:49:30.675 Example:
2025-07-02 05:49:30.682
2025-07-02 05:49:30.688 >>> d = Differ()
2025-07-02 05:49:30.695 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:30.705 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:30.718 >>> print(''.join(results), end="")
2025-07-02 05:49:30.729 - abcDefghiJkl
2025-07-02 05:49:30.751 + abcdefGhijkl
2025-07-02 05:49:30.772 """
2025-07-02 05:49:30.785
2025-07-02 05:49:30.797 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:30.811 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:30.820 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:30.829 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:30.838 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:30.849
2025-07-02 05:49:30.862 # search for the pair that matches best without being identical
2025-07-02 05:49:30.874 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:30.883 # on junk -- unless we have to)
2025-07-02 05:49:30.890 for j in range(blo, bhi):
2025-07-02 05:49:30.897 bj = b[j]
2025-07-02 05:49:30.902 cruncher.set_seq2(bj)
2025-07-02 05:49:30.908 for i in range(alo, ahi):
2025-07-02 05:49:30.915 ai = a[i]
2025-07-02 05:49:30.924 if ai == bj:
2025-07-02 05:49:30.931 if eqi is None:
2025-07-02 05:49:30.939 eqi, eqj = i, j
2025-07-02 05:49:30.951 continue
2025-07-02 05:49:30.959 cruncher.set_seq1(ai)
2025-07-02 05:49:30.967 # computing similarity is expensive, so use the quick
2025-07-02 05:49:30.974 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:30.980 # compares by a factor of 3.
2025-07-02 05:49:30.986 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:30.996 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:31.007 # of the computation is cached by cruncher
2025-07-02 05:49:31.015 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:31.022 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:31.030 cruncher.ratio() > best_ratio:
2025-07-02 05:49:31.042 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:31.051 if best_ratio < cutoff:
2025-07-02 05:49:31.058 # no non-identical "pretty close" pair
2025-07-02 05:49:31.068 if eqi is None:
2025-07-02 05:49:31.078 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:31.087 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:31.095 return
2025-07-02 05:49:31.110 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:31.122 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:31.133 else:
2025-07-02 05:49:31.143 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:31.157 eqi = None
2025-07-02 05:49:31.168
2025-07-02 05:49:31.180 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:31.191 # identical
2025-07-02 05:49:31.199
2025-07-02 05:49:31.208 # pump out diffs from before the synch point
2025-07-02 05:49:31.215 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:31.223
2025-07-02 05:49:31.235 # do intraline marking on the synch pair
2025-07-02 05:49:31.245 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:31.255 if eqi is None:
2025-07-02 05:49:31.264 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:31.271 atags = btags = ""
2025-07-02 05:49:31.278 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:31.291 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:31.302 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:31.313 if tag == 'replace':
2025-07-02 05:49:31.323 atags += '^' * la
2025-07-02 05:49:31.332 btags += '^' * lb
2025-07-02 05:49:31.340 elif tag == 'delete':
2025-07-02 05:49:31.347 atags += '-' * la
2025-07-02 05:49:31.359 elif tag == 'insert':
2025-07-02 05:49:31.370 btags += '+' * lb
2025-07-02 05:49:31.379 elif tag == 'equal':
2025-07-02 05:49:31.392 atags += ' ' * la
2025-07-02 05:49:31.404 btags += ' ' * lb
2025-07-02 05:49:31.417 else:
2025-07-02 05:49:31.430 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:31.442 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:31.455 else:
2025-07-02 05:49:31.467 # the synch pair is identical
2025-07-02 05:49:31.480 yield '  ' + aelt
2025-07-02 05:49:31.494
2025-07-02 05:49:31.506 # pump out diffs from after the synch point
2025-07-02 05:49:31.516 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:31.523
2025-07-02 05:49:31.531 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:31.538 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:31.550
2025-07-02 05:49:31.559 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:31.570 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:31.583 alo = 259, ahi = 1101
2025-07-02 05:49:31.595 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:31.604 blo = 259, bhi = 1101
2025-07-02 05:49:31.612
2025-07-02 05:49:31.624 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:31.637 g = []
2025-07-02 05:49:31.650 if alo < ahi:
2025-07-02 05:49:31.661 if blo < bhi:
2025-07-02 05:49:31.671 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:31.680 else:
2025-07-02 05:49:31.691 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:31.706 elif blo < bhi:
2025-07-02 05:49:31.716 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:31.724
2025-07-02 05:49:31.730 >       yield from g
2025-07-02 05:49:31.736
2025-07-02 05:49:31.742 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:31.747 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:31.752
2025-07-02 05:49:31.758 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:31.764 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:31.770 alo = 259, ahi = 1101
2025-07-02 05:49:31.777 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:31.783 blo = 259, bhi = 1101
2025-07-02 05:49:31.788
2025-07-02 05:49:31.801 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:31.811 r"""
2025-07-02 05:49:31.819 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:31.827 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:31.834 synch point, and intraline difference marking is done on the
2025-07-02 05:49:31.841 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:31.847
2025-07-02 05:49:31.853 Example:
2025-07-02 05:49:31.858
2025-07-02 05:49:31.862 >>> d = Differ()
2025-07-02 05:49:31.869 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:31.876 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:31.883 >>> print(''.join(results), end="")
2025-07-02 05:49:31.889 - abcDefghiJkl
2025-07-02 05:49:31.900 + abcdefGhijkl
2025-07-02 05:49:31.915 """
2025-07-02 05:49:31.927
2025-07-02 05:49:31.939 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:31.951 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:31.961 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:31.973 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:31.985 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:31.997
2025-07-02 05:49:32.005 # search for the pair that matches best without being identical
2025-07-02 05:49:32.011 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:32.018 # on junk -- unless we have to)
2025-07-02 05:49:32.029 for j in range(blo, bhi):
2025-07-02 05:49:32.039 bj = b[j]
2025-07-02 05:49:32.046 cruncher.set_seq2(bj)
2025-07-02 05:49:32.054 for i in range(alo, ahi):
2025-07-02 05:49:32.059 ai = a[i]
2025-07-02 05:49:32.064 if ai == bj:
2025-07-02 05:49:32.069 if eqi is None:
2025-07-02 05:49:32.080 eqi, eqj = i, j
2025-07-02 05:49:32.088 continue
2025-07-02 05:49:32.096 cruncher.set_seq1(ai)
2025-07-02 05:49:32.103 # computing similarity is expensive, so use the quick
2025-07-02 05:49:32.111 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:32.122 # compares by a factor of 3.
2025-07-02 05:49:32.136 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:32.145 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:32.157 # of the computation is cached by cruncher
2025-07-02 05:49:32.166 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:32.176 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:32.186 cruncher.ratio() > best_ratio:
2025-07-02 05:49:32.200 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:32.211 if best_ratio < cutoff:
2025-07-02 05:49:32.220 # no non-identical "pretty close" pair
2025-07-02 05:49:32.228 if eqi is None:
2025-07-02 05:49:32.235 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:32.242 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:32.248 return
2025-07-02 05:49:32.254 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:32.260 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:32.266 else:
2025-07-02 05:49:32.280 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:32.292 eqi = None
2025-07-02 05:49:32.301
2025-07-02 05:49:32.309 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:32.315 # identical
2025-07-02 05:49:32.321
2025-07-02 05:49:32.326 # pump out diffs from before the synch point
2025-07-02 05:49:32.331 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:32.336
2025-07-02 05:49:32.343 # do intraline marking on the synch pair
2025-07-02 05:49:32.352 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:32.361 if eqi is None:
2025-07-02 05:49:32.368 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:32.375 atags = btags = ""
2025-07-02 05:49:32.382 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:32.394 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:32.402 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:32.409 if tag == 'replace':
2025-07-02 05:49:32.418 atags += '^' * la
2025-07-02 05:49:32.427 btags += '^' * lb
2025-07-02 05:49:32.437 elif tag == 'delete':
2025-07-02 05:49:32.445 atags += '-' * la
2025-07-02 05:49:32.453 elif tag == 'insert':
2025-07-02 05:49:32.460 btags += '+' * lb
2025-07-02 05:49:32.466 elif tag == 'equal':
2025-07-02 05:49:32.472 atags += ' ' * la
2025-07-02 05:49:32.478 btags += ' ' * lb
2025-07-02 05:49:32.489 else:
2025-07-02 05:49:32.501 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:32.515 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:32.527 else:
2025-07-02 05:49:32.535 # the synch pair is identical
2025-07-02 05:49:32.543 yield '  ' + aelt
2025-07-02 05:49:32.555
2025-07-02 05:49:32.565 # pump out diffs from after the synch point
2025-07-02 05:49:32.577 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:32.586
2025-07-02 05:49:32.595 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:32.604 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:32.611
2025-07-02 05:49:32.619 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:32.632 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:32.644 alo = 260, ahi = 1101
2025-07-02 05:49:32.652 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:32.659 blo = 260, bhi = 1101
2025-07-02 05:49:32.666
2025-07-02 05:49:32.676 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:32.689 g = []
2025-07-02 05:49:32.697 if alo < ahi:
2025-07-02 05:49:32.704 if blo < bhi:
2025-07-02 05:49:32.711 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:32.717 else:
2025-07-02 05:49:32.729 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:32.737 elif blo < bhi:
2025-07-02 05:49:32.745 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:32.751
2025-07-02 05:49:32.758 >       yield from g
2025-07-02 05:49:32.768
2025-07-02 05:49:32.778 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:32.787 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:32.794
2025-07-02 05:49:32.804 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:32.818 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:32.828 alo = 260, ahi = 1101
2025-07-02 05:49:32.842 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:32.854 blo = 260, bhi = 1101
2025-07-02 05:49:32.864
2025-07-02 05:49:32.873 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:32.885 r"""
2025-07-02 05:49:32.896 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:32.904 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:32.911 synch point, and intraline difference marking is done on the
2025-07-02 05:49:32.918 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:32.931
2025-07-02 05:49:32.940 Example:
2025-07-02 05:49:32.947
2025-07-02 05:49:32.954 >>> d = Differ()
2025-07-02 05:49:32.965 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:32.974 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:32.985 >>> print(''.join(results), end="")
2025-07-02 05:49:32.995 - abcDefghiJkl
2025-07-02 05:49:33.015 + abcdefGhijkl
2025-07-02 05:49:33.033 """
2025-07-02 05:49:33.039
2025-07-02 05:49:33.046 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:33.057 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:33.068 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:33.077 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:33.088 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:33.101
2025-07-02 05:49:33.115 # search for the pair that matches best without being identical
2025-07-02 05:49:33.124 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:33.133 # on junk -- unless we have to)
2025-07-02 05:49:33.144 for j in range(blo, bhi):
2025-07-02 05:49:33.155 bj = b[j]
2025-07-02 05:49:33.167 cruncher.set_seq2(bj)
2025-07-02 05:49:33.178 for i in range(alo, ahi):
2025-07-02 05:49:33.185 ai = a[i]
2025-07-02 05:49:33.191 if ai == bj:
2025-07-02 05:49:33.196 if eqi is None:
2025-07-02 05:49:33.209 eqi, eqj = i, j
2025-07-02 05:49:33.218 continue
2025-07-02 05:49:33.227 cruncher.set_seq1(ai)
2025-07-02 05:49:33.237 # computing similarity is expensive, so use the quick
2025-07-02 05:49:33.244 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:33.250 # compares by a factor of 3.
2025-07-02 05:49:33.261 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:33.272 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:33.281 # of the computation is cached by cruncher
2025-07-02 05:49:33.292 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:33.303 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:33.314 cruncher.ratio() > best_ratio:
2025-07-02 05:49:33.325 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:33.338 if best_ratio < cutoff:
2025-07-02 05:49:33.348 # no non-identical "pretty close" pair
2025-07-02 05:49:33.360 if eqi is None:
2025-07-02 05:49:33.373 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:33.385 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:33.396 return
2025-07-02 05:49:33.404 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:33.414 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:33.424 else:
2025-07-02 05:49:33.436 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:33.446 eqi = None
2025-07-02 05:49:33.453
2025-07-02 05:49:33.460 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:33.466 # identical
2025-07-02 05:49:33.475
2025-07-02 05:49:33.484 # pump out diffs from before the synch point
2025-07-02 05:49:33.495 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:33.505
2025-07-02 05:49:33.515 # do intraline marking on the synch pair
2025-07-02 05:49:33.524 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:33.532 if eqi is None:
2025-07-02 05:49:33.538 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:33.545 atags = btags = ""
2025-07-02 05:49:33.551 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:33.558 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:33.564 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:33.570 if tag == 'replace':
2025-07-02 05:49:33.580 atags += '^' * la
2025-07-02 05:49:33.590 btags += '^' * lb
2025-07-02 05:49:33.597 elif tag == 'delete':
2025-07-02 05:49:33.603 atags += '-' * la
2025-07-02 05:49:33.611 elif tag == 'insert':
2025-07-02 05:49:33.622 btags += '+' * lb
2025-07-02 05:49:33.630 elif tag == 'equal':
2025-07-02 05:49:33.637 atags += ' ' * la
2025-07-02 05:49:33.643 btags += ' ' * lb
2025-07-02 05:49:33.649 else:
2025-07-02 05:49:33.655 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:33.663 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:33.674 else:
2025-07-02 05:49:33.682 # the synch pair is identical
2025-07-02 05:49:33.690 yield '  ' + aelt
2025-07-02 05:49:33.704
2025-07-02 05:49:33.713 # pump out diffs from after the synch point
2025-07-02 05:49:33.720 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:33.726
2025-07-02 05:49:33.733 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:33.739 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:33.748
2025-07-02 05:49:33.755 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:33.762 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:33.767 alo = 261, ahi = 1101
2025-07-02 05:49:33.773 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:33.777 blo = 261, bhi = 1101
2025-07-02 05:49:33.782
2025-07-02 05:49:33.787 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:33.792 g = []
2025-07-02 05:49:33.797 if alo < ahi:
2025-07-02 05:49:33.802 if blo < bhi:
2025-07-02 05:49:33.812 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:33.821 else:
2025-07-02 05:49:33.829 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:33.841 elif blo < bhi:
2025-07-02 05:49:33.851 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:33.860
2025-07-02 05:49:33.867 >       yield from g
2025-07-02 05:49:33.875
2025-07-02 05:49:33.886 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:33.896 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:33.904
2025-07-02 05:49:33.910 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:33.917 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:33.923 alo = 261, ahi = 1101
2025-07-02 05:49:33.933 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:33.945 blo = 261, bhi = 1101
2025-07-02 05:49:33.956
2025-07-02 05:49:33.965 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:33.977 r"""
2025-07-02 05:49:33.989 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:34.000 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:34.011 synch point, and intraline difference marking is done on the
2025-07-02 05:49:34.024 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:34.034
2025-07-02 05:49:34.042 Example:
2025-07-02 05:49:34.053
2025-07-02 05:49:34.064 >>> d = Differ()
2025-07-02 05:49:34.072 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:34.080 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:34.092 >>> print(''.join(results), end="")
2025-07-02 05:49:34.103 - abcDefghiJkl
2025-07-02 05:49:34.120 + abcdefGhijkl
2025-07-02 05:49:34.135 """
2025-07-02 05:49:34.146
2025-07-02 05:49:34.158 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:34.170 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:34.180 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:34.188 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:34.203 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:34.214
2025-07-02 05:49:34.223 # search for the pair that matches best without being identical
2025-07-02 05:49:34.230 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:34.237 # on junk -- unless we have to)
2025-07-02 05:49:34.243 for j in range(blo, bhi):
2025-07-02 05:49:34.249 bj = b[j]
2025-07-02 05:49:34.255 cruncher.set_seq2(bj)
2025-07-02 05:49:34.265 for i in range(alo, ahi):
2025-07-02 05:49:34.277 ai = a[i]
2025-07-02 05:49:34.286 if ai == bj:
2025-07-02 05:49:34.298 if eqi is None:
2025-07-02 05:49:34.310 eqi, eqj = i, j
2025-07-02 05:49:34.321 continue
2025-07-02 05:49:34.329 cruncher.set_seq1(ai)
2025-07-02 05:49:34.340 # computing similarity is expensive, so use the quick
2025-07-02 05:49:34.351 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:34.359 # compares by a factor of 3.
2025-07-02 05:49:34.366 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:34.380 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:34.389 # of the computation is cached by cruncher
2025-07-02 05:49:34.398 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:34.411 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:34.422 cruncher.ratio() > best_ratio:
2025-07-02 05:49:34.431 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:34.438 if best_ratio < cutoff:
2025-07-02 05:49:34.444 # no non-identical "pretty close" pair
2025-07-02 05:49:34.451 if eqi is None:
2025-07-02 05:49:34.458 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:34.463 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:34.470 return
2025-07-02 05:49:34.476 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:34.483 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:34.490 else:
2025-07-02 05:49:34.498 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:34.505 eqi = None
2025-07-02 05:49:34.513
2025-07-02 05:49:34.521 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:34.531 # identical
2025-07-02 05:49:34.541
2025-07-02 05:49:34.552 # pump out diffs from before the synch point
2025-07-02 05:49:34.561 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:34.568
2025-07-02 05:49:34.580 # do intraline marking on the synch pair
2025-07-02 05:49:34.592 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:34.601 if eqi is None:
2025-07-02 05:49:34.610 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:34.623 atags = btags = ""
2025-07-02 05:49:34.636 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:34.648 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:34.660 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:34.673 if tag == 'replace':
2025-07-02 05:49:34.683 atags += '^' * la
2025-07-02 05:49:34.696 btags += '^' * lb
2025-07-02 05:49:34.705 elif tag == 'delete':
2025-07-02 05:49:34.712 atags += '-' * la
2025-07-02 05:49:34.719 elif tag == 'insert':
2025-07-02 05:49:34.725 btags += '+' * lb
2025-07-02 05:49:34.732 elif tag == 'equal':
2025-07-02 05:49:34.740 atags += ' ' * la
2025-07-02 05:49:34.752 btags += ' ' * lb
2025-07-02 05:49:34.762 else:
2025-07-02 05:49:34.771 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:34.786 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:34.796 else:
2025-07-02 05:49:34.804 # the synch pair is identical
2025-07-02 05:49:34.810 yield '  ' + aelt
2025-07-02 05:49:34.821
2025-07-02 05:49:34.832 # pump out diffs from after the synch point
2025-07-02 05:49:34.841 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:34.854
2025-07-02 05:49:34.866 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:34.875 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:34.883
2025-07-02 05:49:34.892 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:34.900 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:34.909 alo = 262, ahi = 1101
2025-07-02 05:49:34.918 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:34.926 blo = 262, bhi = 1101
2025-07-02 05:49:34.933
2025-07-02 05:49:34.939 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:34.945 g = []
2025-07-02 05:49:34.950 if alo < ahi:
2025-07-02 05:49:34.956 if blo < bhi:
2025-07-02 05:49:34.963 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:34.971 else:
2025-07-02 05:49:34.982 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:34.992 elif blo < bhi:
2025-07-02 05:49:35.000 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:35.014
2025-07-02 05:49:35.025 >       yield from g
2025-07-02 05:49:35.037
2025-07-02 05:49:35.046 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:35.054 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:35.061
2025-07-02 05:49:35.067 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:35.077 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:35.082 alo = 262, ahi = 1101
2025-07-02 05:49:35.090 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:35.096 blo = 262, bhi = 1101
2025-07-02 05:49:35.102
2025-07-02 05:49:35.107 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:35.113 r"""
2025-07-02 05:49:35.118 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:35.124 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:35.131 synch point, and intraline difference marking is done on the
2025-07-02 05:49:35.137 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:35.143
2025-07-02 05:49:35.149 Example:
2025-07-02 05:49:35.169
2025-07-02 05:49:35.179 >>> d = Differ()
2025-07-02 05:49:35.186 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:35.193 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:35.199 >>> print(''.join(results), end="")
2025-07-02 05:49:35.206 - abcDefghiJkl
2025-07-02 05:49:35.221 + abcdefGhijkl
2025-07-02 05:49:35.233 """
2025-07-02 05:49:35.239
2025-07-02 05:49:35.246 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:35.252 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:35.259 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:35.268 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:35.279 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:35.292
2025-07-02 05:49:35.301 # search for the pair that matches best without being identical
2025-07-02 05:49:35.308 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:35.315 # on junk -- unless we have to)
2025-07-02 05:49:35.321 for j in range(blo, bhi):
2025-07-02 05:49:35.327 bj = b[j]
2025-07-02 05:49:35.333 cruncher.set_seq2(bj)
2025-07-02 05:49:35.338 for i in range(alo, ahi):
2025-07-02 05:49:35.344 ai = a[i]
2025-07-02 05:49:35.350 if ai == bj:
2025-07-02 05:49:35.361 if eqi is None:
2025-07-02 05:49:35.369 eqi, eqj = i, j
2025-07-02 05:49:35.375 continue
2025-07-02 05:49:35.381 cruncher.set_seq1(ai)
2025-07-02 05:49:35.387 # computing similarity is expensive, so use the quick
2025-07-02 05:49:35.394 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:35.399 # compares by a factor of 3.
2025-07-02 05:49:35.406 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:35.411 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:35.417 # of the computation is cached by cruncher
2025-07-02 05:49:35.423 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:35.430 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:35.435 cruncher.ratio() > best_ratio:
2025-07-02 05:49:35.441 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:35.447 if best_ratio < cutoff:
2025-07-02 05:49:35.455 # no non-identical "pretty close" pair
2025-07-02 05:49:35.465 if eqi is None:
2025-07-02 05:49:35.473 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:35.480 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:35.486 return
2025-07-02 05:49:35.495 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:35.506 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:35.518 else:
2025-07-02 05:49:35.533 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:35.546 eqi = None
2025-07-02 05:49:35.560
2025-07-02 05:49:35.571 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:35.579 # identical
2025-07-02 05:49:35.587
2025-07-02 05:49:35.598 # pump out diffs from before the synch point
2025-07-02 05:49:35.608 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:35.615
2025-07-02 05:49:35.622 # do intraline marking on the synch pair
2025-07-02 05:49:35.631 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:35.643 if eqi is None:
2025-07-02 05:49:35.651 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:35.659 atags = btags = ""
2025-07-02 05:49:35.671 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:35.683 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:35.692 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:35.698 if tag == 'replace':
2025-07-02 05:49:35.705 atags += '^' * la
2025-07-02 05:49:35.711 btags += '^' * lb
2025-07-02 05:49:35.717 elif tag == 'delete':
2025-07-02 05:49:35.723 atags += '-' * la
2025-07-02 05:49:35.728 elif tag == 'insert':
2025-07-02 05:49:35.735 btags += '+' * lb
2025-07-02 05:49:35.740 elif tag == 'equal':
2025-07-02 05:49:35.746 atags += ' ' * la
2025-07-02 05:49:35.756 btags += ' ' * lb
2025-07-02 05:49:35.767 else:
2025-07-02 05:49:35.776 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:35.783 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:35.797 else:
2025-07-02 05:49:35.808 # the synch pair is identical
2025-07-02 05:49:35.819 yield '  ' + aelt
2025-07-02 05:49:35.830
2025-07-02 05:49:35.840 # pump out diffs from after the synch point
2025-07-02 05:49:35.848 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:35.856
2025-07-02 05:49:35.863 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:35.871 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:35.880
2025-07-02 05:49:35.890 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:35.899 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:35.907 alo = 263, ahi = 1101
2025-07-02 05:49:35.914 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:35.924 blo = 263, bhi = 1101
2025-07-02 05:49:35.932
2025-07-02 05:49:35.939 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:35.946 g = []
2025-07-02 05:49:35.951 if alo < ahi:
2025-07-02 05:49:35.957 if blo < bhi:
2025-07-02 05:49:35.964 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:35.971 else:
2025-07-02 05:49:35.978 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:35.989 elif blo < bhi:
2025-07-02 05:49:36.000 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:36.009
2025-07-02 05:49:36.016 >       yield from g
2025-07-02 05:49:36.026
2025-07-02 05:49:36.040 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:36.051 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:36.061
2025-07-02 05:49:36.068 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:36.080 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:36.090 alo = 263, ahi = 1101
2025-07-02 05:49:36.099 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:36.110 blo = 263, bhi = 1101
2025-07-02 05:49:36.118
2025-07-02 05:49:36.125 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:36.137 r"""
2025-07-02 05:49:36.145 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:36.157 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:36.169 synch point, and intraline difference marking is done on the
2025-07-02 05:49:36.178 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:36.185
2025-07-02 05:49:36.191 Example:
2025-07-02 05:49:36.198
2025-07-02 05:49:36.209 >>> d = Differ()
2025-07-02 05:49:36.220 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:36.231 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:36.242 >>> print(''.join(results), end="")
2025-07-02 05:49:36.252 - abcDefghiJkl
2025-07-02 05:49:36.265 + abcdefGhijkl
2025-07-02 05:49:36.275 """
2025-07-02 05:49:36.280
2025-07-02 05:49:36.291 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:36.304 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:36.313 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:36.321 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:36.328 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:36.334
2025-07-02 05:49:36.341 # search for the pair that matches best without being identical
2025-07-02 05:49:36.347 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:36.357 # on junk -- unless we have to)
2025-07-02 05:49:36.368 for j in range(blo, bhi):
2025-07-02 05:49:36.376 bj = b[j]
2025-07-02 05:49:36.383 cruncher.set_seq2(bj)
2025-07-02 05:49:36.392 for i in range(alo, ahi):
2025-07-02 05:49:36.401 ai = a[i]
2025-07-02 05:49:36.409 if ai == bj:
2025-07-02 05:49:36.415 if eqi is None:
2025-07-02 05:49:36.422 eqi, eqj = i, j
2025-07-02 05:49:36.435 continue
2025-07-02 05:49:36.445 cruncher.set_seq1(ai)
2025-07-02 05:49:36.453 # computing similarity is expensive, so use the quick
2025-07-02 05:49:36.462 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:36.473 # compares by a factor of 3.
2025-07-02 05:49:36.487 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:36.501 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:36.514 # of the computation is cached by cruncher
2025-07-02 05:49:36.525 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:36.535 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:36.543 cruncher.ratio() > best_ratio:
2025-07-02 05:49:36.550 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:36.557 if best_ratio < cutoff:
2025-07-02 05:49:36.569 # no non-identical "pretty close" pair
2025-07-02 05:49:36.579 if eqi is None:
2025-07-02 05:49:36.586 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:36.597 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:36.608 return
2025-07-02 05:49:36.616 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:36.624 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:36.630 else:
2025-07-02 05:49:36.642 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:36.651 eqi = None
2025-07-02 05:49:36.659
2025-07-02 05:49:36.667 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:36.679 # identical
2025-07-02 05:49:36.689
2025-07-02 05:49:36.696 # pump out diffs from before the synch point
2025-07-02 05:49:36.703 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:36.710
2025-07-02 05:49:36.716 # do intraline marking on the synch pair
2025-07-02 05:49:36.722 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:36.728 if eqi is None:
2025-07-02 05:49:36.735 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:36.741 atags = btags = ""
2025-07-02 05:49:36.746 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:36.755 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:36.762 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:36.770 if tag == 'replace':
2025-07-02 05:49:36.776 atags += '^' * la
2025-07-02 05:49:36.782 btags += '^' * lb
2025-07-02 05:49:36.788 elif tag == 'delete':
2025-07-02 05:49:36.793 atags += '-' * la
2025-07-02 05:49:36.799 elif tag == 'insert':
2025-07-02 05:49:36.806 btags += '+' * lb
2025-07-02 05:49:36.814 elif tag == 'equal':
2025-07-02 05:49:36.821 atags += ' ' * la
2025-07-02 05:49:36.829 btags += ' ' * lb
2025-07-02 05:49:36.836 else:
2025-07-02 05:49:36.843 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:36.850 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:36.855 else:
2025-07-02 05:49:36.860 # the synch pair is identical
2025-07-02 05:49:36.866 yield '  ' + aelt
2025-07-02 05:49:36.872
2025-07-02 05:49:36.879 # pump out diffs from after the synch point
2025-07-02 05:49:36.885 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:36.893
2025-07-02 05:49:36.901 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:36.911 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:36.921
2025-07-02 05:49:36.930 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:36.937 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:36.943 alo = 264, ahi = 1101
2025-07-02 05:49:36.950 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:36.957 blo = 264, bhi = 1101
2025-07-02 05:49:36.963
2025-07-02 05:49:36.971 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:36.981 g = []
2025-07-02 05:49:36.989 if alo < ahi:
2025-07-02 05:49:36.994 if blo < bhi:
2025-07-02 05:49:37.000 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:37.004 else:
2025-07-02 05:49:37.009 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:37.014 elif blo < bhi:
2025-07-02 05:49:37.019 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:37.023
2025-07-02 05:49:37.029 >       yield from g
2025-07-02 05:49:37.034
2025-07-02 05:49:37.039 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:37.044 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:37.049
2025-07-02 05:49:37.053 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:37.059 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:37.063 alo = 264, ahi = 1101
2025-07-02 05:49:37.069 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:37.074 blo = 264, bhi = 1101
2025-07-02 05:49:37.079
2025-07-02 05:49:37.084 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:37.089 r"""
2025-07-02 05:49:37.095 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:37.101 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:37.107 synch point, and intraline difference marking is done on the
2025-07-02 05:49:37.114 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:37.120
2025-07-02 05:49:37.126 Example:
2025-07-02 05:49:37.132
2025-07-02 05:49:37.138 >>> d = Differ()
2025-07-02 05:49:37.144 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:37.158 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:37.169 >>> print(''.join(results), end="")
2025-07-02 05:49:37.178 - abcDefghiJkl
2025-07-02 05:49:37.199 + abcdefGhijkl
2025-07-02 05:49:37.219 """
2025-07-02 05:49:37.226
2025-07-02 05:49:37.234 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:37.245 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:37.256 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:37.266 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:37.278 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:37.286
2025-07-02 05:49:37.295 # search for the pair that matches best without being identical
2025-07-02 05:49:37.306 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:37.317 # on junk -- unless we have to)
2025-07-02 05:49:37.330 for j in range(blo, bhi):
2025-07-02 05:49:37.342 bj = b[j]
2025-07-02 05:49:37.350 cruncher.set_seq2(bj)
2025-07-02 05:49:37.357 for i in range(alo, ahi):
2025-07-02 05:49:37.364 ai = a[i]
2025-07-02 05:49:37.370 if ai == bj:
2025-07-02 05:49:37.382 if eqi is None:
2025-07-02 05:49:37.393 eqi, eqj = i, j
2025-07-02 05:49:37.400 continue
2025-07-02 05:49:37.407 cruncher.set_seq1(ai)
2025-07-02 05:49:37.414 # computing similarity is expensive, so use the quick
2025-07-02 05:49:37.420 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:37.427 # compares by a factor of 3.
2025-07-02 05:49:37.435 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:37.446 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:37.455 # of the computation is cached by cruncher
2025-07-02 05:49:37.462 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:37.472 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:37.481 cruncher.ratio() > best_ratio:
2025-07-02 05:49:37.492 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:37.502 if best_ratio < cutoff:
2025-07-02 05:49:37.514 # no non-identical "pretty close" pair
2025-07-02 05:49:37.521 if eqi is None:
2025-07-02 05:49:37.527 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:37.535 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:37.546 return
2025-07-02 05:49:37.559 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:37.570 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:37.582 else:
2025-07-02 05:49:37.593 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:37.601 eqi = None
2025-07-02 05:49:37.613
2025-07-02 05:49:37.626 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:37.633 # identical
2025-07-02 05:49:37.638
2025-07-02 05:49:37.651 # pump out diffs from before the synch point
2025-07-02 05:49:37.664 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:37.676
2025-07-02 05:49:37.688 # do intraline marking on the synch pair
2025-07-02 05:49:37.697 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:37.705 if eqi is None:
2025-07-02 05:49:37.710 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:37.718 atags = btags = ""
2025-07-02 05:49:37.725 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:37.730 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:37.741 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:37.751 if tag == 'replace':
2025-07-02 05:49:37.759 atags += '^' * la
2025-07-02 05:49:37.767 btags += '^' * lb
2025-07-02 05:49:37.778 elif tag == 'delete':
2025-07-02 05:49:37.787 atags += '-' * la
2025-07-02 05:49:37.801 elif tag == 'insert':
2025-07-02 05:49:37.813 btags += '+' * lb
2025-07-02 05:49:37.822 elif tag == 'equal':
2025-07-02 05:49:37.831 atags += ' ' * la
2025-07-02 05:49:37.838 btags += ' ' * lb
2025-07-02 05:49:37.844 else:
2025-07-02 05:49:37.851 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:37.859 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:37.871 else:
2025-07-02 05:49:37.880 # the synch pair is identical
2025-07-02 05:49:37.887 yield '  ' + aelt
2025-07-02 05:49:37.893
2025-07-02 05:49:37.899 # pump out diffs from after the synch point
2025-07-02 05:49:37.906 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:37.918
2025-07-02 05:49:37.927 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:37.934 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:37.941
2025-07-02 05:49:37.946 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:37.952 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:37.959 alo = 265, ahi = 1101
2025-07-02 05:49:37.972 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:37.981 blo = 265, bhi = 1101
2025-07-02 05:49:37.989
2025-07-02 05:49:37.995 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:38.000 g = []
2025-07-02 05:49:38.007 if alo < ahi:
2025-07-02 05:49:38.018 if blo < bhi:
2025-07-02 05:49:38.028 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:38.040 else:
2025-07-02 05:49:38.049 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:38.057 elif blo < bhi:
2025-07-02 05:49:38.064 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:38.070
2025-07-02 05:49:38.082 >       yield from g
2025-07-02 05:49:38.090
2025-07-02 05:49:38.102 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:38.112 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:38.123
2025-07-02 05:49:38.130 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:38.137 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:38.144 alo = 265, ahi = 1101
2025-07-02 05:49:38.150 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:38.156 blo = 265, bhi = 1101
2025-07-02 05:49:38.162
2025-07-02 05:49:38.171 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:38.182 r"""
2025-07-02 05:49:38.189 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:38.196 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:38.209 synch point, and intraline difference marking is done on the
2025-07-02 05:49:38.222 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:38.232
2025-07-02 05:49:38.240 Example:
2025-07-02 05:49:38.248
2025-07-02 05:49:38.262 >>> d = Differ()
2025-07-02 05:49:38.274 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:38.283 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:38.291 >>> print(''.join(results), end="")
2025-07-02 05:49:38.300 - abcDefghiJkl
2025-07-02 05:49:38.312 + abcdefGhijkl
2025-07-02 05:49:38.327 """
2025-07-02 05:49:38.337
2025-07-02 05:49:38.345 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:38.350 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:38.356 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:38.362 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:38.371 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:38.383
2025-07-02 05:49:38.393 # search for the pair that matches best without being identical
2025-07-02 05:49:38.399 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:38.404 # on junk -- unless we have to)
2025-07-02 05:49:38.410 for j in range(blo, bhi):
2025-07-02 05:49:38.418 bj = b[j]
2025-07-02 05:49:38.428 cruncher.set_seq2(bj)
2025-07-02 05:49:38.435 for i in range(alo, ahi):
2025-07-02 05:49:38.440 ai = a[i]
2025-07-02 05:49:38.446 if ai == bj:
2025-07-02 05:49:38.458 if eqi is None:
2025-07-02 05:49:38.470 eqi, eqj = i, j
2025-07-02 05:49:38.483 continue
2025-07-02 05:49:38.496 cruncher.set_seq1(ai)
2025-07-02 05:49:38.505 # computing similarity is expensive, so use the quick
2025-07-02 05:49:38.513 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:38.520 # compares by a factor of 3.
2025-07-02 05:49:38.527 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:38.534 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:38.541 # of the computation is cached by cruncher
2025-07-02 05:49:38.547 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:38.554 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:38.565 cruncher.ratio() > best_ratio:
2025-07-02 05:49:38.574 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:38.584 if best_ratio < cutoff:
2025-07-02 05:49:38.594 # no non-identical "pretty close" pair
2025-07-02 05:49:38.604 if eqi is None:
2025-07-02 05:49:38.612 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:38.622 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:38.634 return
2025-07-02 05:49:38.643 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:38.651 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:38.659 else:
2025-07-02 05:49:38.671 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:38.680 eqi = None
2025-07-02 05:49:38.688
2025-07-02 05:49:38.700 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:38.710 # identical
2025-07-02 05:49:38.718
2025-07-02 05:49:38.725 # pump out diffs from before the synch point
2025-07-02 05:49:38.732 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:38.739
2025-07-02 05:49:38.751 # do intraline marking on the synch pair
2025-07-02 05:49:38.760 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:38.768 if eqi is None:
2025-07-02 05:49:38.775 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:38.783 atags = btags = ""
2025-07-02 05:49:38.790 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:38.798 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:38.805 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:38.812 if tag == 'replace':
2025-07-02 05:49:38.820 atags += '^' * la
2025-07-02 05:49:38.826 btags += '^' * lb
2025-07-02 05:49:38.833 elif tag == 'delete':
2025-07-02 05:49:38.840 atags += '-' * la
2025-07-02 05:49:38.847 elif tag == 'insert':
2025-07-02 05:49:38.854 btags += '+' * lb
2025-07-02 05:49:38.861 elif tag == 'equal':
2025-07-02 05:49:38.868 atags += ' ' * la
2025-07-02 05:49:38.878 btags += ' ' * lb
2025-07-02 05:49:38.889 else:
2025-07-02 05:49:38.899 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:38.907 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:38.914 else:
2025-07-02 05:49:38.923 # the synch pair is identical
2025-07-02 05:49:38.933 yield '  ' + aelt
2025-07-02 05:49:38.942
2025-07-02 05:49:38.951 # pump out diffs from after the synch point
2025-07-02 05:49:38.960 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:38.969
2025-07-02 05:49:38.979 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:38.993 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:39.002
2025-07-02 05:49:39.014 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:39.023 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:39.030 alo = 268, ahi = 1101
2025-07-02 05:49:39.038 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:39.045 blo = 268, bhi = 1101
2025-07-02 05:49:39.051
2025-07-02 05:49:39.064 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:39.076 g = []
2025-07-02 05:49:39.084 if alo < ahi:
2025-07-02 05:49:39.091 if blo < bhi:
2025-07-02 05:49:39.098 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:39.110 else:
2025-07-02 05:49:39.119 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:39.127 elif blo < bhi:
2025-07-02 05:49:39.134 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:39.140
2025-07-02 05:49:39.146 >       yield from g
2025-07-02 05:49:39.151
2025-07-02 05:49:39.160 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:39.171 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:39.181
2025-07-02 05:49:39.192 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:39.202 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:39.209 alo = 268, ahi = 1101
2025-07-02 05:49:39.222 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:39.233 blo = 268, bhi = 1101
2025-07-02 05:49:39.239
2025-07-02 05:49:39.245 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:39.249 r"""
2025-07-02 05:49:39.254 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:39.259 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:39.264 synch point, and intraline difference marking is done on the
2025-07-02 05:49:39.276 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:39.285
2025-07-02 05:49:39.295 Example:
2025-07-02 05:49:39.306
2025-07-02 05:49:39.315 >>> d = Differ()
2025-07-02 05:49:39.324 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:39.332 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:39.339 >>> print(''.join(results), end="")
2025-07-02 05:49:39.345 - abcDefghiJkl
2025-07-02 05:49:39.357 + abcdefGhijkl
2025-07-02 05:49:39.370 """
2025-07-02 05:49:39.381
2025-07-02 05:49:39.391 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:39.399 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:39.407 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:39.414 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:39.421 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:39.426
2025-07-02 05:49:39.433 # search for the pair that matches best without being identical
2025-07-02 05:49:39.439 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:39.453 # on junk -- unless we have to)
2025-07-02 05:49:39.463 for j in range(blo, bhi):
2025-07-02 05:49:39.472 bj = b[j]
2025-07-02 05:49:39.480 cruncher.set_seq2(bj)
2025-07-02 05:49:39.487 for i in range(alo, ahi):
2025-07-02 05:49:39.493 ai = a[i]
2025-07-02 05:49:39.504 if ai == bj:
2025-07-02 05:49:39.514 if eqi is None:
2025-07-02 05:49:39.523 eqi, eqj = i, j
2025-07-02 05:49:39.531 continue
2025-07-02 05:49:39.539 cruncher.set_seq1(ai)
2025-07-02 05:49:39.545 # computing similarity is expensive, so use the quick
2025-07-02 05:49:39.551 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:39.557 # compares by a factor of 3.
2025-07-02 05:49:39.563 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:39.570 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:39.575 # of the computation is cached by cruncher
2025-07-02 05:49:39.582 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:39.588 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:39.598 cruncher.ratio() > best_ratio:
2025-07-02 05:49:39.610 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:39.618 if best_ratio < cutoff:
2025-07-02 05:49:39.630 # no non-identical "pretty close" pair
2025-07-02 05:49:39.639 if eqi is None:
2025-07-02 05:49:39.647 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:39.654 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:39.661 return
2025-07-02 05:49:39.673 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:39.682 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:39.691 else:
2025-07-02 05:49:39.703 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:39.712 eqi = None
2025-07-02 05:49:39.720
2025-07-02 05:49:39.727 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:39.735 # identical
2025-07-02 05:49:39.741
2025-07-02 05:49:39.747 # pump out diffs from before the synch point
2025-07-02 05:49:39.755 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:39.762
2025-07-02 05:49:39.771 # do intraline marking on the synch pair
2025-07-02 05:49:39.779 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:39.791 if eqi is None:
2025-07-02 05:49:39.800 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:39.809 atags = btags = ""
2025-07-02 05:49:39.815 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:39.821 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:39.833 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:39.843 if tag == 'replace':
2025-07-02 05:49:39.850 atags += '^' * la
2025-07-02 05:49:39.857 btags += '^' * lb
2025-07-02 05:49:39.864 elif tag == 'delete':
2025-07-02 05:49:39.871 atags += '-' * la
2025-07-02 05:49:39.878 elif tag == 'insert':
2025-07-02 05:49:39.883 btags += '+' * lb
2025-07-02 05:49:39.889 elif tag == 'equal':
2025-07-02 05:49:39.895 atags += ' ' * la
2025-07-02 05:49:39.900 btags += ' ' * lb
2025-07-02 05:49:39.905 else:
2025-07-02 05:49:39.910 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:39.915 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:39.920 else:
2025-07-02 05:49:39.925 # the synch pair is identical
2025-07-02 05:49:39.930 yield '  ' + aelt
2025-07-02 05:49:39.935
2025-07-02 05:49:39.941 # pump out diffs from after the synch point
2025-07-02 05:49:39.946 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:39.952
2025-07-02 05:49:39.962 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:39.975 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:39.986
2025-07-02 05:49:39.996 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:40.004 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:40.012 alo = 269, ahi = 1101
2025-07-02 05:49:40.027 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:40.038 blo = 269, bhi = 1101
2025-07-02 05:49:40.046
2025-07-02 05:49:40.055 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:40.062 g = []
2025-07-02 05:49:40.074 if alo < ahi:
2025-07-02 05:49:40.086 if blo < bhi:
2025-07-02 05:49:40.094 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:40.107 else:
2025-07-02 05:49:40.117 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:40.127 elif blo < bhi:
2025-07-02 05:49:40.135 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:40.148
2025-07-02 05:49:40.159 >       yield from g
2025-07-02 05:49:40.171
2025-07-02 05:49:40.185 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:40.197 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:40.209
2025-07-02 05:49:40.221 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:40.233 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:40.240 alo = 269, ahi = 1101
2025-07-02 05:49:40.249 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:40.255 blo = 269, bhi = 1101
2025-07-02 05:49:40.269
2025-07-02 05:49:40.280 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:40.292 r"""
2025-07-02 05:49:40.302 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:40.310 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:40.317 synch point, and intraline difference marking is done on the
2025-07-02 05:49:40.329 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:40.338
2025-07-02 05:49:40.346 Example:
2025-07-02 05:49:40.359
2025-07-02 05:49:40.366 >>> d = Differ()
2025-07-02 05:49:40.376 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:40.384 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:40.391 >>> print(''.join(results), end="")
2025-07-02 05:49:40.398 - abcDefghiJkl
2025-07-02 05:49:40.415 + abcdefGhijkl
2025-07-02 05:49:40.429 """
2025-07-02 05:49:40.434
2025-07-02 05:49:40.439 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:40.444 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:40.450 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:40.461 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:40.474 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:40.484
2025-07-02 05:49:40.492 # search for the pair that matches best without being identical
2025-07-02 05:49:40.499 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:40.504 # on junk -- unless we have to)
2025-07-02 05:49:40.509 for j in range(blo, bhi):
2025-07-02 05:49:40.514 bj = b[j]
2025-07-02 05:49:40.520 cruncher.set_seq2(bj)
2025-07-02 05:49:40.527 for i in range(alo, ahi):
2025-07-02 05:49:40.535 ai = a[i]
2025-07-02 05:49:40.543 if ai == bj:
2025-07-02 05:49:40.551 if eqi is None:
2025-07-02 05:49:40.559 eqi, eqj = i, j
2025-07-02 05:49:40.571 continue
2025-07-02 05:49:40.579 cruncher.set_seq1(ai)
2025-07-02 05:49:40.589 # computing similarity is expensive, so use the quick
2025-07-02 05:49:40.598 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:40.605 # compares by a factor of 3.
2025-07-02 05:49:40.612 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:40.618 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:40.631 # of the computation is cached by cruncher
2025-07-02 05:49:40.645 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:40.656 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:40.667 cruncher.ratio() > best_ratio:
2025-07-02 05:49:40.676 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:40.686 if best_ratio < cutoff:
2025-07-02 05:49:40.694 # no non-identical "pretty close" pair
2025-07-02 05:49:40.703 if eqi is None:
2025-07-02 05:49:40.715 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:40.723 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:40.730 return
2025-07-02 05:49:40.738 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:40.745 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:40.751 else:
2025-07-02 05:49:40.758 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:40.763 eqi = None
2025-07-02 05:49:40.772
2025-07-02 05:49:40.786 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:40.796 # identical
2025-07-02 05:49:40.804
2025-07-02 05:49:40.811 # pump out diffs from before the synch point
2025-07-02 05:49:40.818 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:40.824
2025-07-02 05:49:40.833 # do intraline marking on the synch pair
2025-07-02 05:49:40.847 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:40.860 if eqi is None:
2025-07-02 05:49:40.869 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:40.880 atags = btags = ""
2025-07-02 05:49:40.890 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:40.898 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:40.905 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:40.914 if tag == 'replace':
2025-07-02 05:49:40.926 atags += '^' * la
2025-07-02 05:49:40.935 btags += '^' * lb
2025-07-02 05:49:40.946 elif tag == 'delete':
2025-07-02 05:49:40.958 atags += '-' * la
2025-07-02 05:49:40.968 elif tag == 'insert':
2025-07-02 05:49:40.976 btags += '+' * lb
2025-07-02 05:49:40.986 elif tag == 'equal':
2025-07-02 05:49:40.993 atags += ' ' * la
2025-07-02 05:49:41.001 btags += ' ' * lb
2025-07-02 05:49:41.012 else:
2025-07-02 05:49:41.022 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:41.030 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:41.035 else:
2025-07-02 05:49:41.041 # the synch pair is identical
2025-07-02 05:49:41.047 yield '  ' + aelt
2025-07-02 05:49:41.053
2025-07-02 05:49:41.059 # pump out diffs from after the synch point
2025-07-02 05:49:41.067 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:41.075
2025-07-02 05:49:41.087 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:41.097 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:41.109
2025-07-02 05:49:41.117 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:41.125 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:41.131 alo = 270, ahi = 1101
2025-07-02 05:49:41.139 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:41.145 blo = 270, bhi = 1101
2025-07-02 05:49:41.150
2025-07-02 05:49:41.161 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:41.171 g = []
2025-07-02 05:49:41.179 if alo < ahi:
2025-07-02 05:49:41.189 if blo < bhi:
2025-07-02 05:49:41.197 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:41.205 else:
2025-07-02 05:49:41.217 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:41.230 elif blo < bhi:
2025-07-02 05:49:41.241 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:41.251
2025-07-02 05:49:41.261 >       yield from g
2025-07-02 05:49:41.272
2025-07-02 05:49:41.282 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:41.292 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:41.305
2025-07-02 05:49:41.315 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:41.329 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:41.338 alo = 270, ahi = 1101
2025-07-02 05:49:41.348 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:41.361 blo = 270, bhi = 1101
2025-07-02 05:49:41.372
2025-07-02 05:49:41.383 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:41.389 r"""
2025-07-02 05:49:41.394 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:41.405 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:41.414 synch point, and intraline difference marking is done on the
2025-07-02 05:49:41.422 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:41.432
2025-07-02 05:49:41.445 Example:
2025-07-02 05:49:41.458
2025-07-02 05:49:41.470 >>> d = Differ()
2025-07-02 05:49:41.484 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:41.496 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:41.508 >>> print(''.join(results), end="")
2025-07-02 05:49:41.521 - abcDefghiJkl
2025-07-02 05:49:41.538 + abcdefGhijkl
2025-07-02 05:49:41.556 """
2025-07-02 05:49:41.565
2025-07-02 05:49:41.573 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:41.581 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:41.587 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:41.594 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:41.604 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:41.613
2025-07-02 05:49:41.621 # search for the pair that matches best without being identical
2025-07-02 05:49:41.628 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:41.635 # on junk -- unless we have to)
2025-07-02 05:49:41.643 for j in range(blo, bhi):
2025-07-02 05:49:41.654 bj = b[j]
2025-07-02 05:49:41.663 cruncher.set_seq2(bj)
2025-07-02 05:49:41.671 for i in range(alo, ahi):
2025-07-02 05:49:41.678 ai = a[i]
2025-07-02 05:49:41.687 if ai == bj:
2025-07-02 05:49:41.697 if eqi is None:
2025-07-02 05:49:41.705 eqi, eqj = i, j
2025-07-02 05:49:41.720 continue
2025-07-02 05:49:41.730 cruncher.set_seq1(ai)
2025-07-02 05:49:41.740 # computing similarity is expensive, so use the quick
2025-07-02 05:49:41.751 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:41.761 # compares by a factor of 3.
2025-07-02 05:49:41.775 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:41.787 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:41.798 # of the computation is cached by cruncher
2025-07-02 05:49:41.810 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:41.821 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:41.834 cruncher.ratio() > best_ratio:
2025-07-02 05:49:41.843 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:41.851 if best_ratio < cutoff:
2025-07-02 05:49:41.859 # no non-identical "pretty close" pair
2025-07-02 05:49:41.867 if eqi is None:
2025-07-02 05:49:41.879 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:41.889 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:41.901 return
2025-07-02 05:49:41.914 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:41.924 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:41.933 else:
2025-07-02 05:49:41.939 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:41.947 eqi = None
2025-07-02 05:49:41.957
2025-07-02 05:49:41.965 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:41.973 # identical
2025-07-02 05:49:41.979
2025-07-02 05:49:41.987 # pump out diffs from before the synch point
2025-07-02 05:49:41.995 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:42.006
2025-07-02 05:49:42.014 # do intraline marking on the synch pair
2025-07-02 05:49:42.022 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:42.031 if eqi is None:
2025-07-02 05:49:42.043 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:42.052 atags = btags = ""
2025-07-02 05:49:42.060 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:42.067 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:42.076 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:42.083 if tag == 'replace':
2025-07-02 05:49:42.091 atags += '^' * la
2025-07-02 05:49:42.103 btags += '^' * lb
2025-07-02 05:49:42.117 elif tag == 'delete':
2025-07-02 05:49:42.127 atags += '-' * la
2025-07-02 05:49:42.135 elif tag == 'insert':
2025-07-02 05:49:42.143 btags += '+' * lb
2025-07-02 05:49:42.154 elif tag == 'equal':
2025-07-02 05:49:42.163 atags += ' ' * la
2025-07-02 05:49:42.174 btags += ' ' * lb
2025-07-02 05:49:42.185 else:
2025-07-02 05:49:42.196 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:42.209 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:42.216 else:
2025-07-02 05:49:42.224 # the synch pair is identical
2025-07-02 05:49:42.233 yield '  ' + aelt
2025-07-02 05:49:42.244
2025-07-02 05:49:42.258 # pump out diffs from after the synch point
2025-07-02 05:49:42.267 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:42.275
2025-07-02 05:49:42.282 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:42.293 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:42.303
2025-07-02 05:49:42.311 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:42.321 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:42.328 alo = 271, ahi = 1101
2025-07-02 05:49:42.336 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:42.343 blo = 271, bhi = 1101
2025-07-02 05:49:42.351
2025-07-02 05:49:42.362 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:42.369 g = []
2025-07-02 05:49:42.376 if alo < ahi:
2025-07-02 05:49:42.383 if blo < bhi:
2025-07-02 05:49:42.390 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:42.396 else:
2025-07-02 05:49:42.402 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:42.410 elif blo < bhi:
2025-07-02 05:49:42.418 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:42.426
2025-07-02 05:49:42.432 >       yield from g
2025-07-02 05:49:42.443
2025-07-02 05:49:42.453 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:42.462 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:42.469
2025-07-02 05:49:42.480 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:42.492 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:42.505 alo = 271, ahi = 1101
2025-07-02 05:49:42.516 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:42.523 blo = 271, bhi = 1101
2025-07-02 05:49:42.531
2025-07-02 05:49:42.545 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:42.554 r"""
2025-07-02 05:49:42.561 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:42.567 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:42.575 synch point, and intraline difference marking is done on the
2025-07-02 05:49:42.584 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:42.591
2025-07-02 05:49:42.597 Example:
2025-07-02 05:49:42.602
2025-07-02 05:49:42.612 >>> d = Differ()
2025-07-02 05:49:42.621 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:42.632 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:42.643 >>> print(''.join(results), end="")
2025-07-02 05:49:42.652 - abcDefghiJkl
2025-07-02 05:49:42.671 + abcdefGhijkl
2025-07-02 05:49:42.688 """
2025-07-02 05:49:42.697
2025-07-02 05:49:42.706 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:42.714 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:42.721 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:42.727 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:42.735 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:42.746
2025-07-02 05:49:42.753 # search for the pair that matches best without being identical
2025-07-02 05:49:42.765 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:42.773 # on junk -- unless we have to)
2025-07-02 05:49:42.784 for j in range(blo, bhi):
2025-07-02 05:49:42.797 bj = b[j]
2025-07-02 05:49:42.808 cruncher.set_seq2(bj)
2025-07-02 05:49:42.819 for i in range(alo, ahi):
2025-07-02 05:49:42.830 ai = a[i]
2025-07-02 05:49:42.842 if ai == bj:
2025-07-02 05:49:42.853 if eqi is None:
2025-07-02 05:49:42.862 eqi, eqj = i, j
2025-07-02 05:49:42.872 continue
2025-07-02 05:49:42.884 cruncher.set_seq1(ai)
2025-07-02 05:49:42.895 # computing similarity is expensive, so use the quick
2025-07-02 05:49:42.903 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:42.910 # compares by a factor of 3.
2025-07-02 05:49:42.923 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:42.933 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:42.943 # of the computation is cached by cruncher
2025-07-02 05:49:42.958 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:42.967 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:42.975 cruncher.ratio() > best_ratio:
2025-07-02 05:49:42.987 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:42.997 if best_ratio < cutoff:
2025-07-02 05:49:43.010 # no non-identical "pretty close" pair
2025-07-02 05:49:43.021 if eqi is None:
2025-07-02 05:49:43.030 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:43.039 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:43.050 return
2025-07-02 05:49:43.060 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:43.068 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:43.075 else:
2025-07-02 05:49:43.083 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:43.090 eqi = None
2025-07-02 05:49:43.098
2025-07-02 05:49:43.104 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:43.113 # identical
2025-07-02 05:49:43.124
2025-07-02 05:49:43.135 # pump out diffs from before the synch point
2025-07-02 05:49:43.145 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:43.155
2025-07-02 05:49:43.168 # do intraline marking on the synch pair
2025-07-02 05:49:43.176 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:43.183 if eqi is None:
2025-07-02 05:49:43.190 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:43.196 atags = btags = ""
2025-07-02 05:49:43.202 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:43.209 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:43.215 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:43.228 if tag == 'replace':
2025-07-02 05:49:43.241 atags += '^' * la
2025-07-02 05:49:43.251 btags += '^' * lb
2025-07-02 05:49:43.258 elif tag == 'delete':
2025-07-02 05:49:43.270 atags += '-' * la
2025-07-02 05:49:43.283 elif tag == 'insert':
2025-07-02 05:49:43.292 btags += '+' * lb
2025-07-02 05:49:43.299 elif tag == 'equal':
2025-07-02 05:49:43.305 atags += ' ' * la
2025-07-02 05:49:43.315 btags += ' ' * lb
2025-07-02 05:49:43.321 else:
2025-07-02 05:49:43.328 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:43.335 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:43.341 else:
2025-07-02 05:49:43.347 # the synch pair is identical
2025-07-02 05:49:43.361 yield '  ' + aelt
2025-07-02 05:49:43.373
2025-07-02 05:49:43.385 # pump out diffs from after the synch point
2025-07-02 05:49:43.397 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:43.404
2025-07-02 05:49:43.411 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:43.418 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:43.424
2025-07-02 05:49:43.430 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:43.435 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:43.439 alo = 272, ahi = 1101
2025-07-02 05:49:43.445 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:43.451 blo = 272, bhi = 1101
2025-07-02 05:49:43.456
2025-07-02 05:49:43.460 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:43.465 g = []
2025-07-02 05:49:43.473 if alo < ahi:
2025-07-02 05:49:43.480 if blo < bhi:
2025-07-02 05:49:43.487 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:43.494 else:
2025-07-02 05:49:43.504 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:43.516 elif blo < bhi:
2025-07-02 05:49:43.526 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:43.536
2025-07-02 05:49:43.543 >       yield from g
2025-07-02 05:49:43.551
2025-07-02 05:49:43.563 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:43.573 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:43.581
2025-07-02 05:49:43.589 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:43.599 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:43.610 alo = 272, ahi = 1101
2025-07-02 05:49:43.622 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:43.631 blo = 272, bhi = 1101
2025-07-02 05:49:43.644
2025-07-02 05:49:43.655 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:43.666 r"""
2025-07-02 05:49:43.677 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:43.690 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:43.701 synch point, and intraline difference marking is done on the
2025-07-02 05:49:43.713 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:43.724
2025-07-02 05:49:43.733 Example:
2025-07-02 05:49:43.744
2025-07-02 05:49:43.757 >>> d = Differ()
2025-07-02 05:49:43.771 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:43.783 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:43.793 >>> print(''.join(results), end="")
2025-07-02 05:49:43.803 - abcDefghiJkl
2025-07-02 05:49:43.823 + abcdefGhijkl
2025-07-02 05:49:43.839 """
2025-07-02 05:49:43.846
2025-07-02 05:49:43.853 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:43.859 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:43.874 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:43.885 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:43.893 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:43.899
2025-07-02 05:49:43.906 # search for the pair that matches best without being identical
2025-07-02 05:49:43.913 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:43.919 # on junk -- unless we have to)
2025-07-02 05:49:43.926 for j in range(blo, bhi):
2025-07-02 05:49:43.934 bj = b[j]
2025-07-02 05:49:43.942 cruncher.set_seq2(bj)
2025-07-02 05:49:43.949 for i in range(alo, ahi):
2025-07-02 05:49:43.954 ai = a[i]
2025-07-02 05:49:43.960 if ai == bj:
2025-07-02 05:49:43.969 if eqi is None:
2025-07-02 05:49:43.979 eqi, eqj = i, j
2025-07-02 05:49:43.989 continue
2025-07-02 05:49:43.997 cruncher.set_seq1(ai)
2025-07-02 05:49:44.005 # computing similarity is expensive, so use the quick
2025-07-02 05:49:44.012 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:44.018 # compares by a factor of 3.
2025-07-02 05:49:44.024 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:44.030 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:44.037 # of the computation is cached by cruncher
2025-07-02 05:49:44.046 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:44.053 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:44.058 cruncher.ratio() > best_ratio:
2025-07-02 05:49:44.064 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:44.070 if best_ratio < cutoff:
2025-07-02 05:49:44.083 # no non-identical "pretty close" pair
2025-07-02 05:49:44.090 if eqi is None:
2025-07-02 05:49:44.098 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:44.110 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:44.119 return
2025-07-02 05:49:44.128 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:44.135 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:44.143 else:
2025-07-02 05:49:44.155 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:44.163 eqi = None
2025-07-02 05:49:44.171
2025-07-02 05:49:44.179 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:44.190 # identical
2025-07-02 05:49:44.200
2025-07-02 05:49:44.208 # pump out diffs from before the synch point
2025-07-02 05:49:44.216 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:44.222
2025-07-02 05:49:44.233 # do intraline marking on the synch pair
2025-07-02 05:49:44.243 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:44.251 if eqi is None:
2025-07-02 05:49:44.258 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:44.266 atags = btags = ""
2025-07-02 05:49:44.272 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:44.278 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:44.284 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:44.290 if tag == 'replace':
2025-07-02 05:49:44.296 atags += '^' * la
2025-07-02 05:49:44.305 btags += '^' * lb
2025-07-02 05:49:44.315 elif tag == 'delete':
2025-07-02 05:49:44.326 atags += '-' * la
2025-07-02 05:49:44.338 elif tag == 'insert':
2025-07-02 05:49:44.350 btags += '+' * lb
2025-07-02 05:49:44.361 elif tag == 'equal':
2025-07-02 05:49:44.369 atags += ' ' * la
2025-07-02 05:49:44.382 btags += ' ' * lb
2025-07-02 05:49:44.394 else:
2025-07-02 05:49:44.404 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:44.414 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:44.420 else:
2025-07-02 05:49:44.426 # the synch pair is identical
2025-07-02 05:49:44.431 yield '  ' + aelt
2025-07-02 05:49:44.437
2025-07-02 05:49:44.442 # pump out diffs from after the synch point
2025-07-02 05:49:44.446 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:44.451
2025-07-02 05:49:44.458 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:44.468 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:44.480
2025-07-02 05:49:44.489 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:44.497 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:44.503 alo = 273, ahi = 1101
2025-07-02 05:49:44.511 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:44.519 blo = 273, bhi = 1101
2025-07-02 05:49:44.533
2025-07-02 05:49:44.543 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:44.551 g = []
2025-07-02 05:49:44.559 if alo < ahi:
2025-07-02 05:49:44.569 if blo < bhi:
2025-07-02 05:49:44.579 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:44.586 else:
2025-07-02 05:49:44.592 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:44.598 elif blo < bhi:
2025-07-02 05:49:44.604 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:44.610
2025-07-02 05:49:44.616 >       yield from g
2025-07-02 05:49:44.622
2025-07-02 05:49:44.628 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:44.641 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:44.650
2025-07-02 05:49:44.659 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:44.672 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:44.684 alo = 273, ahi = 1101
2025-07-02 05:49:44.695 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:44.705 blo = 273, bhi = 1101
2025-07-02 05:49:44.717
2025-07-02 05:49:44.726 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:44.734 r"""
2025-07-02 05:49:44.743 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:44.750 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:44.757 synch point, and intraline difference marking is done on the
2025-07-02 05:49:44.764 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:44.771
2025-07-02 05:49:44.779 Example:
2025-07-02 05:49:44.787
2025-07-02 05:49:44.795 >>> d = Differ()
2025-07-02 05:49:44.805 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:44.813 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:44.820 >>> print(''.join(results), end="")
2025-07-02 05:49:44.827 - abcDefghiJkl
2025-07-02 05:49:44.846 + abcdefGhijkl
2025-07-02 05:49:44.863 """
2025-07-02 05:49:44.870
2025-07-02 05:49:44.878 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:44.885 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:44.893 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:44.900 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:44.906 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:44.918
2025-07-02 05:49:44.929 # search for the pair that matches best without being identical
2025-07-02 05:49:44.938 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:44.946 # on junk -- unless we have to)
2025-07-02 05:49:44.954 for j in range(blo, bhi):
2025-07-02 05:49:44.965 bj = b[j]
2025-07-02 05:49:44.976 cruncher.set_seq2(bj)
2025-07-02 05:49:44.986 for i in range(alo, ahi):
2025-07-02 05:49:44.998 ai = a[i]
2025-07-02 05:49:45.010 if ai == bj:
2025-07-02 05:49:45.019 if eqi is None:
2025-07-02 05:49:45.027 eqi, eqj = i, j
2025-07-02 05:49:45.035 continue
2025-07-02 05:49:45.044 cruncher.set_seq1(ai)
2025-07-02 05:49:45.055 # computing similarity is expensive, so use the quick
2025-07-02 05:49:45.063 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:45.070 # compares by a factor of 3.
2025-07-02 05:49:45.082 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:45.091 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:45.098 # of the computation is cached by cruncher
2025-07-02 05:49:45.106 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:45.119 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:45.127 cruncher.ratio() > best_ratio:
2025-07-02 05:49:45.134 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:45.141 if best_ratio < cutoff:
2025-07-02 05:49:45.147 # no non-identical "pretty close" pair
2025-07-02 05:49:45.152 if eqi is None:
2025-07-02 05:49:45.158 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:45.164 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:45.170 return
2025-07-02 05:49:45.182 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:45.192 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:45.200 else:
2025-07-02 05:49:45.207 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:45.217 eqi = None
2025-07-02 05:49:45.224
2025-07-02 05:49:45.231 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:45.237 # identical
2025-07-02 05:49:45.244
2025-07-02 05:49:45.250 # pump out diffs from before the synch point
2025-07-02 05:49:45.257 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:45.262
2025-07-02 05:49:45.268 # do intraline marking on the synch pair
2025-07-02 05:49:45.275 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:45.286 if eqi is None:
2025-07-02 05:49:45.295 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:45.302 atags = btags = ""
2025-07-02 05:49:45.311 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:45.320 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:45.328 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:45.334 if tag == 'replace':
2025-07-02 05:49:45.340 atags += '^' * la
2025-07-02 05:49:45.346 btags += '^' * lb
2025-07-02 05:49:45.358 elif tag == 'delete':
2025-07-02 05:49:45.369 atags += '-' * la
2025-07-02 05:49:45.381 elif tag == 'insert':
2025-07-02 05:49:45.392 btags += '+' * lb
2025-07-02 05:49:45.401 elif tag == 'equal':
2025-07-02 05:49:45.408 atags += ' ' * la
2025-07-02 05:49:45.416 btags += ' ' * lb
2025-07-02 05:49:45.422 else:
2025-07-02 05:49:45.429 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:45.435 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:45.443 else:
2025-07-02 05:49:45.453 # the synch pair is identical
2025-07-02 05:49:45.462 yield '  ' + aelt
2025-07-02 05:49:45.467
2025-07-02 05:49:45.473 # pump out diffs from after the synch point
2025-07-02 05:49:45.479 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:45.490
2025-07-02 05:49:45.503 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:45.515 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:45.527
2025-07-02 05:49:45.542 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:45.551 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:45.562 alo = 274, ahi = 1101
2025-07-02 05:49:45.576 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:45.587 blo = 274, bhi = 1101
2025-07-02 05:49:45.595
2025-07-02 05:49:45.602 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:45.611 g = []
2025-07-02 05:49:45.621 if alo < ahi:
2025-07-02 05:49:45.634 if blo < bhi:
2025-07-02 05:49:45.646 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:45.657 else:
2025-07-02 05:49:45.670 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:45.682 elif blo < bhi:
2025-07-02 05:49:45.691 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:45.698
2025-07-02 05:49:45.707 >       yield from g
2025-07-02 05:49:45.715
2025-07-02 05:49:45.723 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:45.734 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:45.742
2025-07-02 05:49:45.751 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:45.763 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:45.770 alo = 274, ahi = 1101
2025-07-02 05:49:45.777 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:45.783 blo = 274, bhi = 1101
2025-07-02 05:49:45.789
2025-07-02 05:49:45.798 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:45.810 r"""
2025-07-02 05:49:45.821 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:45.828 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:45.834 synch point, and intraline difference marking is done on the
2025-07-02 05:49:45.840 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:45.846
2025-07-02 05:49:45.857 Example:
2025-07-02 05:49:45.867
2025-07-02 05:49:45.874 >>> d = Differ()
2025-07-02 05:49:45.881 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:45.888 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:45.894 >>> print(''.join(results), end="")
2025-07-02 05:49:45.901 - abcDefghiJkl
2025-07-02 05:49:45.915 + abcdefGhijkl
2025-07-02 05:49:45.933 """
2025-07-02 05:49:45.945
2025-07-02 05:49:45.953 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:45.960 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:45.965 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:45.975 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:45.985 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:45.991
2025-07-02 05:49:45.998 # search for the pair that matches best without being identical
2025-07-02 05:49:46.004 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:46.011 # on junk -- unless we have to)
2025-07-02 05:49:46.018 for j in range(blo, bhi):
2025-07-02 05:49:46.024 bj = b[j]
2025-07-02 05:49:46.032 cruncher.set_seq2(bj)
2025-07-02 05:49:46.039 for i in range(alo, ahi):
2025-07-02 05:49:46.047 ai = a[i]
2025-07-02 05:49:46.059 if ai == bj:
2025-07-02 05:49:46.069 if eqi is None:
2025-07-02 05:49:46.078 eqi, eqj = i, j
2025-07-02 05:49:46.086 continue
2025-07-02 05:49:46.093 cruncher.set_seq1(ai)
2025-07-02 05:49:46.101 # computing similarity is expensive, so use the quick
2025-07-02 05:49:46.108 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:46.115 # compares by a factor of 3.
2025-07-02 05:49:46.123 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:46.131 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:46.138 # of the computation is cached by cruncher
2025-07-02 05:49:46.147 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:46.158 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:46.167 cruncher.ratio() > best_ratio:
2025-07-02 05:49:46.174 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:46.180 if best_ratio < cutoff:
2025-07-02 05:49:46.187 # no non-identical "pretty close" pair
2025-07-02 05:49:46.195 if eqi is None:
2025-07-02 05:49:46.206 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:46.214 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:46.222 return
2025-07-02 05:49:46.231 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:46.242 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:46.254 else:
2025-07-02 05:49:46.263 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:46.270 eqi = None
2025-07-02 05:49:46.277
2025-07-02 05:49:46.283 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:46.292 # identical
2025-07-02 05:49:46.302
2025-07-02 05:49:46.311 # pump out diffs from before the synch point
2025-07-02 05:49:46.318 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:46.326
2025-07-02 05:49:46.332 # do intraline marking on the synch pair
2025-07-02 05:49:46.339 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:46.346 if eqi is None:
2025-07-02 05:49:46.353 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:46.359 atags = btags = ""
2025-07-02 05:49:46.371 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:46.382 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:46.389 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:46.396 if tag == 'replace':
2025-07-02 05:49:46.403 atags += '^' * la
2025-07-02 05:49:46.409 btags += '^' * lb
2025-07-02 05:49:46.415 elif tag == 'delete':
2025-07-02 05:49:46.423 atags += '-' * la
2025-07-02 05:49:46.429 elif tag == 'insert':
2025-07-02 05:49:46.436 btags += '+' * lb
2025-07-02 05:49:46.443 elif tag == 'equal':
2025-07-02 05:49:46.451 atags += ' ' * la
2025-07-02 05:49:46.458 btags += ' ' * lb
2025-07-02 05:49:46.469 else:
2025-07-02 05:49:46.481 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:46.490 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:46.497 else:
2025-07-02 05:49:46.504 # the synch pair is identical
2025-07-02 05:49:46.511 yield '  ' + aelt
2025-07-02 05:49:46.517
2025-07-02 05:49:46.524 # pump out diffs from after the synch point
2025-07-02 05:49:46.531 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:46.539
2025-07-02 05:49:46.547 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:46.558 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:46.567
2025-07-02 05:49:46.574 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:46.580 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:46.585 alo = 275, ahi = 1101
2025-07-02 05:49:46.590 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:46.595 blo = 275, bhi = 1101
2025-07-02 05:49:46.601
2025-07-02 05:49:46.606 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:46.610 g = []
2025-07-02 05:49:46.616 if alo < ahi:
2025-07-02 05:49:46.622 if blo < bhi:
2025-07-02 05:49:46.629 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:46.634 else:
2025-07-02 05:49:46.641 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:46.647 elif blo < bhi:
2025-07-02 05:49:46.657 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:46.664
2025-07-02 05:49:46.670 >       yield from g
2025-07-02 05:49:46.676
2025-07-02 05:49:46.683 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:46.691 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:46.702
2025-07-02 05:49:46.717 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:46.726 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:46.732 alo = 275, ahi = 1101
2025-07-02 05:49:46.740 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:46.747 blo = 275, bhi = 1101
2025-07-02 05:49:46.752
2025-07-02 05:49:46.758 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:46.764 r"""
2025-07-02 05:49:46.771 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:46.778 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:46.787 synch point, and intraline difference marking is done on the
2025-07-02 05:49:46.795 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:46.803
2025-07-02 05:49:46.815 Example:
2025-07-02 05:49:46.825
2025-07-02 05:49:46.833 >>> d = Differ()
2025-07-02 05:49:46.839 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:46.846 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:46.852 >>> print(''.join(results), end="")
2025-07-02 05:49:46.858 - abcDefghiJkl
2025-07-02 05:49:46.879 + abcdefGhijkl
2025-07-02 05:49:46.894 """
2025-07-02 05:49:46.902
2025-07-02 05:49:46.909 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:46.915 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:46.921 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:46.926 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:46.933 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:46.942
2025-07-02 05:49:46.954 # search for the pair that matches best without being identical
2025-07-02 05:49:46.965 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:46.977 # on junk -- unless we have to)
2025-07-02 05:49:46.990 for j in range(blo, bhi):
2025-07-02 05:49:47.000 bj = b[j]
2025-07-02 05:49:47.008 cruncher.set_seq2(bj)
2025-07-02 05:49:47.017 for i in range(alo, ahi):
2025-07-02 05:49:47.024 ai = a[i]
2025-07-02 05:49:47.032 if ai == bj:
2025-07-02 05:49:47.039 if eqi is None:
2025-07-02 05:49:47.046 eqi, eqj = i, j
2025-07-02 05:49:47.052 continue
2025-07-02 05:49:47.060 cruncher.set_seq1(ai)
2025-07-02 05:49:47.068 # computing similarity is expensive, so use the quick
2025-07-02 05:49:47.074 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:47.081 # compares by a factor of 3.
2025-07-02 05:49:47.088 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:47.095 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:47.104 # of the computation is cached by cruncher
2025-07-02 05:49:47.117 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:47.129 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:47.139 cruncher.ratio() > best_ratio:
2025-07-02 05:49:47.146 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:47.152 if best_ratio < cutoff:
2025-07-02 05:49:47.158 # no non-identical "pretty close" pair
2025-07-02 05:49:47.164 if eqi is None:
2025-07-02 05:49:47.174 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:47.181 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:47.187 return
2025-07-02 05:49:47.193 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:47.201 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:47.208 else:
2025-07-02 05:49:47.219 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:47.228 eqi = None
2025-07-02 05:49:47.236
2025-07-02 05:49:47.245 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:47.256 # identical
2025-07-02 05:49:47.267
2025-07-02 05:49:47.274 # pump out diffs from before the synch point
2025-07-02 05:49:47.281 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:47.288
2025-07-02 05:49:47.296 # do intraline marking on the synch pair
2025-07-02 05:49:47.305 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:47.318 if eqi is None:
2025-07-02 05:49:47.332 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:47.341 atags = btags = ""
2025-07-02 05:49:47.348 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:47.355 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:47.362 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:47.371 if tag == 'replace':
2025-07-02 05:49:47.380 atags += '^' * la
2025-07-02 05:49:47.389 btags += '^' * lb
2025-07-02 05:49:47.397 elif tag == 'delete':
2025-07-02 05:49:47.410 atags += '-' * la
2025-07-02 05:49:47.419 elif tag == 'insert':
2025-07-02 05:49:47.427 btags += '+' * lb
2025-07-02 05:49:47.434 elif tag == 'equal':
2025-07-02 05:49:47.443 atags += ' ' * la
2025-07-02 05:49:47.450 btags += ' ' * lb
2025-07-02 05:49:47.457 else:
2025-07-02 05:49:47.464 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:47.472 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:47.478 else:
2025-07-02 05:49:47.485 # the synch pair is identical
2025-07-02 05:49:47.493 yield '  ' + aelt
2025-07-02 05:49:47.499
2025-07-02 05:49:47.507 # pump out diffs from after the synch point
2025-07-02 05:49:47.514 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:47.521
2025-07-02 05:49:47.528 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:47.535 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:47.542
2025-07-02 05:49:47.549 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:47.556 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:47.562 alo = 276, ahi = 1101
2025-07-02 05:49:47.570 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:47.578 blo = 276, bhi = 1101
2025-07-02 05:49:47.584
2025-07-02 05:49:47.590 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:47.596 g = []
2025-07-02 05:49:47.602 if alo < ahi:
2025-07-02 05:49:47.608 if blo < bhi:
2025-07-02 05:49:47.614 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:47.620 else:
2025-07-02 05:49:47.628 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:47.639 elif blo < bhi:
2025-07-02 05:49:47.647 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:47.656
2025-07-02 05:49:47.662 >       yield from g
2025-07-02 05:49:47.671
2025-07-02 05:49:47.679 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:47.686 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:47.692
2025-07-02 05:49:47.698 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:47.705 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:47.711 alo = 276, ahi = 1101
2025-07-02 05:49:47.717 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:47.723 blo = 276, bhi = 1101
2025-07-02 05:49:47.728
2025-07-02 05:49:47.734 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:47.740 r"""
2025-07-02 05:49:47.746 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:47.752 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:47.758 synch point, and intraline difference marking is done on the
2025-07-02 05:49:47.763 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:47.773
2025-07-02 05:49:47.781 Example:
2025-07-02 05:49:47.788
2025-07-02 05:49:47.793 >>> d = Differ()
2025-07-02 05:49:47.799 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:47.804 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:47.809 >>> print(''.join(results), end="")
2025-07-02 05:49:47.815 - abcDefghiJkl
2025-07-02 05:49:47.829 + abcdefGhijkl
2025-07-02 05:49:47.841 """
2025-07-02 05:49:47.847
2025-07-02 05:49:47.852 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:47.858 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:47.863 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:47.868 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:47.874 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:47.879
2025-07-02 05:49:47.885 # search for the pair that matches best without being identical
2025-07-02 05:49:47.893 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:47.900 # on junk -- unless we have to)
2025-07-02 05:49:47.910 for j in range(blo, bhi):
2025-07-02 05:49:47.919 bj = b[j]
2025-07-02 05:49:47.926 cruncher.set_seq2(bj)
2025-07-02 05:49:47.932 for i in range(alo, ahi):
2025-07-02 05:49:47.939 ai = a[i]
2025-07-02 05:49:47.945 if ai == bj:
2025-07-02 05:49:47.950 if eqi is None:
2025-07-02 05:49:47.955 eqi, eqj = i, j
2025-07-02 05:49:47.968 continue
2025-07-02 05:49:47.979 cruncher.set_seq1(ai)
2025-07-02 05:49:47.991 # computing similarity is expensive, so use the quick
2025-07-02 05:49:48.004 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:48.012 # compares by a factor of 3.
2025-07-02 05:49:48.019 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:48.026 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:48.035 # of the computation is cached by cruncher
2025-07-02 05:49:48.041 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:48.046 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:48.051 cruncher.ratio() > best_ratio:
2025-07-02 05:49:48.056 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:48.061 if best_ratio < cutoff:
2025-07-02 05:49:48.066 # no non-identical "pretty close" pair
2025-07-02 05:49:48.071 if eqi is None:
2025-07-02 05:49:48.076 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:48.081 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:48.086 return
2025-07-02 05:49:48.091 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:48.101 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:48.110 else:
2025-07-02 05:49:48.116 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:48.122 eqi = None
2025-07-02 05:49:48.132
2025-07-02 05:49:48.144 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:48.152 # identical
2025-07-02 05:49:48.159
2025-07-02 05:49:48.168 # pump out diffs from before the synch point
2025-07-02 05:49:48.178 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:48.186
2025-07-02 05:49:48.201 # do intraline marking on the synch pair
2025-07-02 05:49:48.212 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:48.220 if eqi is None:
2025-07-02 05:49:48.227 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:48.233 atags = btags = ""
2025-07-02 05:49:48.239 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:48.253 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:48.263 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:48.271 if tag == 'replace':
2025-07-02 05:49:48.280 atags += '^' * la
2025-07-02 05:49:48.288 btags += '^' * lb
2025-07-02 05:49:48.295 elif tag == 'delete':
2025-07-02 05:49:48.302 atags += '-' * la
2025-07-02 05:49:48.312 elif tag == 'insert':
2025-07-02 05:49:48.325 btags += '+' * lb
2025-07-02 05:49:48.335 elif tag == 'equal':
2025-07-02 05:49:48.343 atags += ' ' * la
2025-07-02 05:49:48.349 btags += ' ' * lb
2025-07-02 05:49:48.355 else:
2025-07-02 05:49:48.363 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:48.369 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:48.376 else:
2025-07-02 05:49:48.383 # the synch pair is identical
2025-07-02 05:49:48.395 yield '  ' + aelt
2025-07-02 05:49:48.405
2025-07-02 05:49:48.416 # pump out diffs from after the synch point
2025-07-02 05:49:48.427 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:48.435
2025-07-02 05:49:48.442 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:48.451 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:48.459
2025-07-02 05:49:48.466 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:48.475 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:48.482 alo = 277, ahi = 1101
2025-07-02 05:49:48.491 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:48.498 blo = 277, bhi = 1101
2025-07-02 05:49:48.508
2025-07-02 05:49:48.520 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:48.530 g = []
2025-07-02 05:49:48.541 if alo < ahi:
2025-07-02 05:49:48.547 if blo < bhi:
2025-07-02 05:49:48.554 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:48.559 else:
2025-07-02 05:49:48.564 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:48.571 elif blo < bhi:
2025-07-02 05:49:48.577 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:48.583
2025-07-02 05:49:48.588 >       yield from g
2025-07-02 05:49:48.594
2025-07-02 05:49:48.605 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:48.616 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:48.625
2025-07-02 05:49:48.636 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:48.651 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:48.662 alo = 277, ahi = 1101
2025-07-02 05:49:48.672 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:48.678 blo = 277, bhi = 1101
2025-07-02 05:49:48.683
2025-07-02 05:49:48.691 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:48.702 r"""
2025-07-02 05:49:48.712 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:48.721 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:48.731 synch point, and intraline difference marking is done on the
2025-07-02 05:49:48.743 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:48.754
2025-07-02 05:49:48.762 Example:
2025-07-02 05:49:48.770
2025-07-02 05:49:48.776 >>> d = Differ()
2025-07-02 05:49:48.783 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:48.790 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:48.797 >>> print(''.join(results), end="")
2025-07-02 05:49:48.803 - abcDefghiJkl
2025-07-02 05:49:48.816 + abcdefGhijkl
2025-07-02 05:49:48.828 """
2025-07-02 05:49:48.838
2025-07-02 05:49:48.845 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:48.851 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:48.859 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:48.866 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:48.873 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:48.879
2025-07-02 05:49:48.887 # search for the pair that matches best without being identical
2025-07-02 05:49:48.899 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:48.908 # on junk -- unless we have to)
2025-07-02 05:49:48.921 for j in range(blo, bhi):
2025-07-02 05:49:48.930 bj = b[j]
2025-07-02 05:49:48.941 cruncher.set_seq2(bj)
2025-07-02 05:49:48.949 for i in range(alo, ahi):
2025-07-02 05:49:48.956 ai = a[i]
2025-07-02 05:49:48.962 if ai == bj:
2025-07-02 05:49:48.969 if eqi is None:
2025-07-02 05:49:48.975 eqi, eqj = i, j
2025-07-02 05:49:48.985 continue
2025-07-02 05:49:48.998 cruncher.set_seq1(ai)
2025-07-02 05:49:49.010 # computing similarity is expensive, so use the quick
2025-07-02 05:49:49.021 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:49.033 # compares by a factor of 3.
2025-07-02 05:49:49.042 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:49.051 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:49.063 # of the computation is cached by cruncher
2025-07-02 05:49:49.071 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:49.078 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:49.085 cruncher.ratio() > best_ratio:
2025-07-02 05:49:49.091 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:49.097 if best_ratio < cutoff:
2025-07-02 05:49:49.103 # no non-identical "pretty close" pair
2025-07-02 05:49:49.109 if eqi is None:
2025-07-02 05:49:49.118 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:49.131 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:49.142 return
2025-07-02 05:49:49.155 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:49.166 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:49.178 else:
2025-07-02 05:49:49.189 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:49.199 eqi = None
2025-07-02 05:49:49.207
2025-07-02 05:49:49.220 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:49.229 # identical
2025-07-02 05:49:49.239
2025-07-02 05:49:49.252 # pump out diffs from before the synch point
2025-07-02 05:49:49.263 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:49.274
2025-07-02 05:49:49.283 # do intraline marking on the synch pair
2025-07-02 05:49:49.294 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:49.305 if eqi is None:
2025-07-02 05:49:49.317 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:49.327 atags = btags = ""
2025-07-02 05:49:49.335 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:49.348 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:49.359 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:49.367 if tag == 'replace':
2025-07-02 05:49:49.374 atags += '^' * la
2025-07-02 05:49:49.380 btags += '^' * lb
2025-07-02 05:49:49.386 elif tag == 'delete':
2025-07-02 05:49:49.396 atags += '-' * la
2025-07-02 05:49:49.409 elif tag == 'insert':
2025-07-02 05:49:49.417 btags += '+' * lb
2025-07-02 05:49:49.430 elif tag == 'equal':
2025-07-02 05:49:49.441 atags += ' ' * la
2025-07-02 05:49:49.449 btags += ' ' * lb
2025-07-02 05:49:49.457 else:
2025-07-02 05:49:49.464 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:49.471 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:49.477 else:
2025-07-02 05:49:49.483 # the synch pair is identical
2025-07-02 05:49:49.491 yield '  ' + aelt
2025-07-02 05:49:49.497
2025-07-02 05:49:49.503 # pump out diffs from after the synch point
2025-07-02 05:49:49.509 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:49.520
2025-07-02 05:49:49.530 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:49.538 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:49.545
2025-07-02 05:49:49.557 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:49.566 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:49.573 alo = 278, ahi = 1101
2025-07-02 05:49:49.587 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:49.596 blo = 278, bhi = 1101
2025-07-02 05:49:49.604
2025-07-02 05:49:49.612 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:49.619 g = []
2025-07-02 05:49:49.625 if alo < ahi:
2025-07-02 05:49:49.629 if blo < bhi:
2025-07-02 05:49:49.634 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:49.638 else:
2025-07-02 05:49:49.643 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:49.649 elif blo < bhi:
2025-07-02 05:49:49.657 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:49.664
2025-07-02 05:49:49.670 >       yield from g
2025-07-02 05:49:49.677
2025-07-02 05:49:49.684 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:49.690 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:49.695
2025-07-02 05:49:49.700 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:49.705 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:49.709 alo = 278, ahi = 1101
2025-07-02 05:49:49.714 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:49.719 blo = 278, bhi = 1101
2025-07-02 05:49:49.723
2025-07-02 05:49:49.727 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:49.732 r"""
2025-07-02 05:49:49.737 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:49.742 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:49.748 synch point, and intraline difference marking is done on the
2025-07-02 05:49:49.754 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:49.759
2025-07-02 05:49:49.767 Example:
2025-07-02 05:49:49.779
2025-07-02 05:49:49.788 >>> d = Differ()
2025-07-02 05:49:49.799 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:49.809 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:49.820 >>> print(''.join(results), end="")
2025-07-02 05:49:49.832 - abcDefghiJkl
2025-07-02 05:49:49.853 + abcdefGhijkl
2025-07-02 05:49:49.873 """
2025-07-02 05:49:49.883
2025-07-02 05:49:49.895 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:49.908 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:49.917 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:49.930 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:49.939 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:49.945
2025-07-02 05:49:49.951 # search for the pair that matches best without being identical
2025-07-02 05:49:49.961 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:49.973 # on junk -- unless we have to)
2025-07-02 05:49:49.985 for j in range(blo, bhi):
2025-07-02 05:49:49.998 bj = b[j]
2025-07-02 05:49:50.008 cruncher.set_seq2(bj)
2025-07-02 05:49:50.017 for i in range(alo, ahi):
2025-07-02 05:49:50.031 ai = a[i]
2025-07-02 05:49:50.044 if ai == bj:
2025-07-02 05:49:50.056 if eqi is None:
2025-07-02 05:49:50.066 eqi, eqj = i, j
2025-07-02 05:49:50.076 continue
2025-07-02 05:49:50.084 cruncher.set_seq1(ai)
2025-07-02 05:49:50.092 # computing similarity is expensive, so use the quick
2025-07-02 05:49:50.099 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:50.105 # compares by a factor of 3.
2025-07-02 05:49:50.118 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:50.127 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:50.134 # of the computation is cached by cruncher
2025-07-02 05:49:50.143 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:50.152 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:50.161 cruncher.ratio() > best_ratio:
2025-07-02 05:49:50.171 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:50.185 if best_ratio < cutoff:
2025-07-02 05:49:50.195 # no non-identical "pretty close" pair
2025-07-02 05:49:50.205 if eqi is None:
2025-07-02 05:49:50.212 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:50.219 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:50.225 return
2025-07-02 05:49:50.231 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:50.239 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:50.250 else:
2025-07-02 05:49:50.260 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:50.267 eqi = None
2025-07-02 05:49:50.274
2025-07-02 05:49:50.284 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:50.293 # identical
2025-07-02 05:49:50.307
2025-07-02 05:49:50.317 # pump out diffs from before the synch point
2025-07-02 05:49:50.327 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:50.338
2025-07-02 05:49:50.347 # do intraline marking on the synch pair
2025-07-02 05:49:50.355 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:50.363 if eqi is None:
2025-07-02 05:49:50.369 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:50.380 atags = btags = ""
2025-07-02 05:49:50.391 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:50.400 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:50.408 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:50.415 if tag == 'replace':
2025-07-02 05:49:50.429 atags += '^' * la
2025-07-02 05:49:50.440 btags += '^' * lb
2025-07-02 05:49:50.452 elif tag == 'delete':
2025-07-02 05:49:50.464 atags += '-' * la
2025-07-02 05:49:50.477 elif tag == 'insert':
2025-07-02 05:49:50.489 btags += '+' * lb
2025-07-02 05:49:50.498 elif tag == 'equal':
2025-07-02 05:49:50.507 atags += ' ' * la
2025-07-02 05:49:50.514 btags += ' ' * lb
2025-07-02 05:49:50.520 else:
2025-07-02 05:49:50.526 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:50.534 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:50.547 else:
2025-07-02 05:49:50.557 # the synch pair is identical
2025-07-02 05:49:50.568 yield '  ' + aelt
2025-07-02 05:49:50.581
2025-07-02 05:49:50.594 # pump out diffs from after the synch point
2025-07-02 05:49:50.604 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:50.613
2025-07-02 05:49:50.625 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:50.634 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:50.642
2025-07-02 05:49:50.657 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:50.669 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:50.677 alo = 279, ahi = 1101
2025-07-02 05:49:50.690 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:50.701 blo = 279, bhi = 1101
2025-07-02 05:49:50.710
2025-07-02 05:49:50.720 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:50.733 g = []
2025-07-02 05:49:50.742 if alo < ahi:
2025-07-02 05:49:50.749 if blo < bhi:
2025-07-02 05:49:50.756 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:50.760 else:
2025-07-02 05:49:50.765 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:50.770 elif blo < bhi:
2025-07-02 05:49:50.774 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:50.779
2025-07-02 05:49:50.783 >       yield from g
2025-07-02 05:49:50.788
2025-07-02 05:49:50.793 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:50.806 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:50.816
2025-07-02 05:49:50.826 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:50.838 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:50.851 alo = 279, ahi = 1101
2025-07-02 05:49:50.867 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:50.877 blo = 279, bhi = 1101
2025-07-02 05:49:50.890
2025-07-02 05:49:50.902 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:50.913 r"""
2025-07-02 05:49:50.928 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:50.939 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:50.951 synch point, and intraline difference marking is done on the
2025-07-02 05:49:50.961 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:50.968
2025-07-02 05:49:50.975 Example:
2025-07-02 05:49:50.983
2025-07-02 05:49:50.993 >>> d = Differ()
2025-07-02 05:49:51.003 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:51.012 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:51.021 >>> print(''.join(results), end="")
2025-07-02 05:49:51.028 - abcDefghiJkl
2025-07-02 05:49:51.049 + abcdefGhijkl
2025-07-02 05:49:51.071 """
2025-07-02 05:49:51.080
2025-07-02 05:49:51.086 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:51.094 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:51.100 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:51.105 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:51.112 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:51.124
2025-07-02 05:49:51.134 # search for the pair that matches best without being identical
2025-07-02 05:49:51.145 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:51.157 # on junk -- unless we have to)
2025-07-02 05:49:51.166 for j in range(blo, bhi):
2025-07-02 05:49:51.177 bj = b[j]
2025-07-02 05:49:51.191 cruncher.set_seq2(bj)
2025-07-02 05:49:51.201 for i in range(alo, ahi):
2025-07-02 05:49:51.210 ai = a[i]
2025-07-02 05:49:51.219 if ai == bj:
2025-07-02 05:49:51.225 if eqi is None:
2025-07-02 05:49:51.230 eqi, eqj = i, j
2025-07-02 05:49:51.241 continue
2025-07-02 05:49:51.252 cruncher.set_seq1(ai)
2025-07-02 05:49:51.260 # computing similarity is expensive, so use the quick
2025-07-02 05:49:51.267 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:51.275 # compares by a factor of 3.
2025-07-02 05:49:51.286 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:51.299 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:51.308 # of the computation is cached by cruncher
2025-07-02 05:49:51.319 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:51.332 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:51.344 cruncher.ratio() > best_ratio:
2025-07-02 05:49:51.357 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:51.367 if best_ratio < cutoff:
2025-07-02 05:49:51.375 # no non-identical "pretty close" pair
2025-07-02 05:49:51.383 if eqi is None:
2025-07-02 05:49:51.395 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:51.404 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:51.412 return
2025-07-02 05:49:51.419 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:51.427 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:51.440 else:
2025-07-02 05:49:51.451 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:51.459 eqi = None
2025-07-02 05:49:51.467
2025-07-02 05:49:51.479 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:51.492 # identical
2025-07-02 05:49:51.503
2025-07-02 05:49:51.514 # pump out diffs from before the synch point
2025-07-02 05:49:51.527 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:51.537
2025-07-02 05:49:51.549 # do intraline marking on the synch pair
2025-07-02 05:49:51.558 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:51.566 if eqi is None:
2025-07-02 05:49:51.578 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:51.590 atags = btags = ""
2025-07-02 05:49:51.598 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:51.607 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:51.615 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:51.623 if tag == 'replace':
2025-07-02 05:49:51.636 atags += '^' * la
2025-07-02 05:49:51.646 btags += '^' * lb
2025-07-02 05:49:51.654 elif tag == 'delete':
2025-07-02 05:49:51.662 atags += '-' * la
2025-07-02 05:49:51.674 elif tag == 'insert':
2025-07-02 05:49:51.682 btags += '+' * lb
2025-07-02 05:49:51.692 elif tag == 'equal':
2025-07-02 05:49:51.703 atags += ' ' * la
2025-07-02 05:49:51.710 btags += ' ' * lb
2025-07-02 05:49:51.717 else:
2025-07-02 05:49:51.725 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:51.733 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:51.739 else:
2025-07-02 05:49:51.745 # the synch pair is identical
2025-07-02 05:49:51.751 yield '  ' + aelt
2025-07-02 05:49:51.759
2025-07-02 05:49:51.769 # pump out diffs from after the synch point
2025-07-02 05:49:51.778 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:51.785
2025-07-02 05:49:51.792 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:51.797 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:51.803
2025-07-02 05:49:51.809 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:51.819 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:51.831 alo = 280, ahi = 1101
2025-07-02 05:49:51.840 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:51.847 blo = 280, bhi = 1101
2025-07-02 05:49:51.855
2025-07-02 05:49:51.862 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:51.869 g = []
2025-07-02 05:49:51.875 if alo < ahi:
2025-07-02 05:49:51.882 if blo < bhi:
2025-07-02 05:49:51.896 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:51.907 else:
2025-07-02 05:49:51.915 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:51.922 elif blo < bhi:
2025-07-02 05:49:51.929 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:51.935
2025-07-02 05:49:51.942 >       yield from g
2025-07-02 05:49:51.954
2025-07-02 05:49:51.964 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:51.977 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:51.987
2025-07-02 05:49:51.998 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:52.007 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:52.015 alo = 280, ahi = 1101
2025-07-02 05:49:52.024 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:52.032 blo = 280, bhi = 1101
2025-07-02 05:49:52.039
2025-07-02 05:49:52.049 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:52.057 r"""
2025-07-02 05:49:52.064 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:52.071 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:52.079 synch point, and intraline difference marking is done on the
2025-07-02 05:49:52.090 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:52.102
2025-07-02 05:49:52.114 Example:
2025-07-02 05:49:52.126
2025-07-02 05:49:52.136 >>> d = Differ()
2025-07-02 05:49:52.145 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:52.152 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:52.160 >>> print(''.join(results), end="")
2025-07-02 05:49:52.167 - abcDefghiJkl
2025-07-02 05:49:52.188 + abcdefGhijkl
2025-07-02 05:49:52.208 """
2025-07-02 05:49:52.216
2025-07-02 05:49:52.229 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:52.238 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:52.247 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:52.253 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:52.263 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:52.271
2025-07-02 05:49:52.279 # search for the pair that matches best without being identical
2025-07-02 05:49:52.286 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:52.293 # on junk -- unless we have to)
2025-07-02 05:49:52.299 for j in range(blo, bhi):
2025-07-02 05:49:52.305 bj = b[j]
2025-07-02 05:49:52.312 cruncher.set_seq2(bj)
2025-07-02 05:49:52.317 for i in range(alo, ahi):
2025-07-02 05:49:52.326 ai = a[i]
2025-07-02 05:49:52.334 if ai == bj:
2025-07-02 05:49:52.342 if eqi is None:
2025-07-02 05:49:52.349 eqi, eqj = i, j
2025-07-02 05:49:52.355 continue
2025-07-02 05:49:52.363 cruncher.set_seq1(ai)
2025-07-02 05:49:52.371 # computing similarity is expensive, so use the quick
2025-07-02 05:49:52.383 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:52.394 # compares by a factor of 3.
2025-07-02 05:49:52.404 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:52.413 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:52.420 # of the computation is cached by cruncher
2025-07-02 05:49:52.428 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:52.446 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:52.457 cruncher.ratio() > best_ratio:
2025-07-02 05:49:52.466 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:52.474 if best_ratio < cutoff:
2025-07-02 05:49:52.482 # no non-identical "pretty close" pair
2025-07-02 05:49:52.489 if eqi is None:
2025-07-02 05:49:52.496 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:52.502 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:52.511 return
2025-07-02 05:49:52.524 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:52.534 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:52.542 else:
2025-07-02 05:49:52.554 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:52.563 eqi = None
2025-07-02 05:49:52.572
2025-07-02 05:49:52.583 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:52.591 # identical
2025-07-02 05:49:52.597
2025-07-02 05:49:52.603 # pump out diffs from before the synch point
2025-07-02 05:49:52.613 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:52.620
2025-07-02 05:49:52.627 # do intraline marking on the synch pair
2025-07-02 05:49:52.640 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:52.649 if eqi is None:
2025-07-02 05:49:52.660 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:52.670 atags = btags = ""
2025-07-02 05:49:52.678 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:52.687 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:52.693 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:52.699 if tag == 'replace':
2025-07-02 05:49:52.709 atags += '^' * la
2025-07-02 05:49:52.720 btags += '^' * lb
2025-07-02 05:49:52.732 elif tag == 'delete':
2025-07-02 05:49:52.745 atags += '-' * la
2025-07-02 05:49:52.757 elif tag == 'insert':
2025-07-02 05:49:52.768 btags += '+' * lb
2025-07-02 05:49:52.777 elif tag == 'equal':
2025-07-02 05:49:52.785 atags += ' ' * la
2025-07-02 05:49:52.792 btags += ' ' * lb
2025-07-02 05:49:52.804 else:
2025-07-02 05:49:52.813 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:52.820 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:52.826 else:
2025-07-02 05:49:52.837 # the synch pair is identical
2025-07-02 05:49:52.848 yield '  ' + aelt
2025-07-02 05:49:52.857
2025-07-02 05:49:52.865 # pump out diffs from after the synch point
2025-07-02 05:49:52.871 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:52.876
2025-07-02 05:49:52.881 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:52.886 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:52.894
2025-07-02 05:49:52.905 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:52.915 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:52.923 alo = 281, ahi = 1101
2025-07-02 05:49:52.932 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:52.943 blo = 281, bhi = 1101
2025-07-02 05:49:52.956
2025-07-02 05:49:52.966 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:52.977 g = []
2025-07-02 05:49:52.987 if alo < ahi:
2025-07-02 05:49:53.003 if blo < bhi:
2025-07-02 05:49:53.015 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:53.028 else:
2025-07-02 05:49:53.037 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:53.044 elif blo < bhi:
2025-07-02 05:49:53.052 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:53.059
2025-07-02 05:49:53.064 >       yield from g
2025-07-02 05:49:53.070
2025-07-02 05:49:53.076 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:53.082 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:53.091
2025-07-02 05:49:53.104 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:53.115 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:53.123 alo = 281, ahi = 1101
2025-07-02 05:49:53.137 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:53.147 blo = 281, bhi = 1101
2025-07-02 05:49:53.157
2025-07-02 05:49:53.165 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:53.173 r"""
2025-07-02 05:49:53.186 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:53.199 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:53.209 synch point, and intraline difference marking is done on the
2025-07-02 05:49:53.218 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:53.225
2025-07-02 05:49:53.232 Example:
2025-07-02 05:49:53.238
2025-07-02 05:49:53.251 >>> d = Differ()
2025-07-02 05:49:53.261 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:53.274 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:53.285 >>> print(''.join(results), end="")
2025-07-02 05:49:53.294 - abcDefghiJkl
2025-07-02 05:49:53.312 + abcdefGhijkl
2025-07-02 05:49:53.326 """
2025-07-02 05:49:53.333
2025-07-02 05:49:53.339 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:53.347 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:53.358 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:53.367 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:53.386 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:53.400
2025-07-02 05:49:53.415 # search for the pair that matches best without being identical
2025-07-02 05:49:53.428 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:53.439 # on junk -- unless we have to)
2025-07-02 05:49:53.447 for j in range(blo, bhi):
2025-07-02 05:49:53.454 bj = b[j]
2025-07-02 05:49:53.461 cruncher.set_seq2(bj)
2025-07-02 05:49:53.475 for i in range(alo, ahi):
2025-07-02 05:49:53.484 ai = a[i]
2025-07-02 05:49:53.493 if ai == bj:
2025-07-02 05:49:53.500 if eqi is None:
2025-07-02 05:49:53.506 eqi, eqj = i, j
2025-07-02 05:49:53.513 continue
2025-07-02 05:49:53.519 cruncher.set_seq1(ai)
2025-07-02 05:49:53.526 # computing similarity is expensive, so use the quick
2025-07-02 05:49:53.537 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:53.546 # compares by a factor of 3.
2025-07-02 05:49:53.553 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:53.567 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:53.577 # of the computation is cached by cruncher
2025-07-02 05:49:53.590 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:53.601 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:53.617 cruncher.ratio() > best_ratio:
2025-07-02 05:49:53.628 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:53.642 if best_ratio < cutoff:
2025-07-02 05:49:53.655 # no non-identical "pretty close" pair
2025-07-02 05:49:53.669 if eqi is None:
2025-07-02 05:49:53.680 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:53.690 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:53.698 return
2025-07-02 05:49:53.712 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:53.727 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:53.739 else:
2025-07-02 05:49:53.747 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:53.754 eqi = None
2025-07-02 05:49:53.760
2025-07-02 05:49:53.767 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:53.774 # identical
2025-07-02 05:49:53.787
2025-07-02 05:49:53.796 # pump out diffs from before the synch point
2025-07-02 05:49:53.803 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:53.808
2025-07-02 05:49:53.813 # do intraline marking on the synch pair
2025-07-02 05:49:53.819 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:53.824 if eqi is None:
2025-07-02 05:49:53.831 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:53.851 atags = btags = ""
2025-07-02 05:49:53.859 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:53.866 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:53.873 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:53.880 if tag == 'replace':
2025-07-02 05:49:53.886 atags += '^' * la
2025-07-02 05:49:53.892 btags += '^' * lb
2025-07-02 05:49:53.898 elif tag == 'delete':
2025-07-02 05:49:53.905 atags += '-' * la
2025-07-02 05:49:53.912 elif tag == 'insert':
2025-07-02 05:49:53.918 btags += '+' * lb
2025-07-02 05:49:53.925 elif tag == 'equal':
2025-07-02 05:49:53.931 atags += ' ' * la
2025-07-02 05:49:53.939 btags += ' ' * lb
2025-07-02 05:49:53.952 else:
2025-07-02 05:49:53.961 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:53.970 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:53.984 else:
2025-07-02 05:49:53.994 # the synch pair is identical
2025-07-02 05:49:54.005 yield '  ' + aelt
2025-07-02 05:49:54.015
2025-07-02 05:49:54.028 # pump out diffs from after the synch point
2025-07-02 05:49:54.039 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:54.047
2025-07-02 05:49:54.056 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:54.067 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:54.081
2025-07-02 05:49:54.093 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:54.103 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:54.111 alo = 282, ahi = 1101
2025-07-02 05:49:54.128 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:54.138 blo = 282, bhi = 1101
2025-07-02 05:49:54.147
2025-07-02 05:49:54.155 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:54.162 g = []
2025-07-02 05:49:54.173 if alo < ahi:
2025-07-02 05:49:54.186 if blo < bhi:
2025-07-02 05:49:54.200 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:54.213 else:
2025-07-02 05:49:54.225 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:54.235 elif blo < bhi:
2025-07-02 05:49:54.246 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:54.252
2025-07-02 05:49:54.258 >       yield from g
2025-07-02 05:49:54.264
2025-07-02 05:49:54.270 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:54.277 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:54.284
2025-07-02 05:49:54.290 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:54.297 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:54.309 alo = 282, ahi = 1101
2025-07-02 05:49:54.320 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:54.328 blo = 282, bhi = 1101
2025-07-02 05:49:54.335
2025-07-02 05:49:54.340 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:54.346 r"""
2025-07-02 05:49:54.352 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:54.358 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:54.363 synch point, and intraline difference marking is done on the
2025-07-02 05:49:54.370 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:54.375
2025-07-02 05:49:54.381 Example:
2025-07-02 05:49:54.387
2025-07-02 05:49:54.392 >>> d = Differ()
2025-07-02 05:49:54.397 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:54.403 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:54.410 >>> print(''.join(results), end="")
2025-07-02 05:49:54.419 - abcDefghiJkl
2025-07-02 05:49:54.432 + abcdefGhijkl
2025-07-02 05:49:54.446 """
2025-07-02 05:49:54.453
2025-07-02 05:49:54.459 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:54.466 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:54.473 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:54.487 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:54.501 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:54.512
2025-07-02 05:49:54.526 # search for the pair that matches best without being identical
2025-07-02 05:49:54.537 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:54.546 # on junk -- unless we have to)
2025-07-02 05:49:54.555 for j in range(blo, bhi):
2025-07-02 05:49:54.568 bj = b[j]
2025-07-02 05:49:54.575 cruncher.set_seq2(bj)
2025-07-02 05:49:54.583 for i in range(alo, ahi):
2025-07-02 05:49:54.592 ai = a[i]
2025-07-02 05:49:54.604 if ai == bj:
2025-07-02 05:49:54.614 if eqi is None:
2025-07-02 05:49:54.625 eqi, eqj = i, j
2025-07-02 05:49:54.634 continue
2025-07-02 05:49:54.644 cruncher.set_seq1(ai)
2025-07-02 05:49:54.654 # computing similarity is expensive, so use the quick
2025-07-02 05:49:54.663 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:54.670 # compares by a factor of 3.
2025-07-02 05:49:54.681 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:54.692 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:54.706 # of the computation is cached by cruncher
2025-07-02 05:49:54.715 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:54.722 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:54.731 cruncher.ratio() > best_ratio:
2025-07-02 05:49:54.738 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:54.744 if best_ratio < cutoff:
2025-07-02 05:49:54.754 # no non-identical "pretty close" pair
2025-07-02 05:49:54.765 if eqi is None:
2025-07-02 05:49:54.774 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:54.781 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:54.788 return
2025-07-02 05:49:54.794 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:54.800 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:54.806 else:
2025-07-02 05:49:54.813 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:54.826 eqi = None
2025-07-02 05:49:54.840
2025-07-02 05:49:54.851 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:54.863 # identical
2025-07-02 05:49:54.876
2025-07-02 05:49:54.887 # pump out diffs from before the synch point
2025-07-02 05:49:54.897 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:54.909
2025-07-02 05:49:54.923 # do intraline marking on the synch pair
2025-07-02 05:49:54.934 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:54.943 if eqi is None:
2025-07-02 05:49:54.951 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:54.959 atags = btags = ""
2025-07-02 05:49:54.967 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:54.979 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:54.988 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:54.997 if tag == 'replace':
2025-07-02 05:49:55.004 atags += '^' * la
2025-07-02 05:49:55.011 btags += '^' * lb
2025-07-02 05:49:55.018 elif tag == 'delete':
2025-07-02 05:49:55.029 atags += '-' * la
2025-07-02 05:49:55.039 elif tag == 'insert':
2025-07-02 05:49:55.049 btags += '+' * lb
2025-07-02 05:49:55.060 elif tag == 'equal':
2025-07-02 05:49:55.070 atags += ' ' * la
2025-07-02 05:49:55.080 btags += ' ' * lb
2025-07-02 05:49:55.087 else:
2025-07-02 05:49:55.094 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:55.101 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:55.107 else:
2025-07-02 05:49:55.116 # the synch pair is identical
2025-07-02 05:49:55.128 yield '  ' + aelt
2025-07-02 05:49:55.137
2025-07-02 05:49:55.143 # pump out diffs from after the synch point
2025-07-02 05:49:55.157 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:55.168
2025-07-02 05:49:55.177 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:55.186 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:55.193
2025-07-02 05:49:55.200 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:55.213 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:55.223 alo = 283, ahi = 1101
2025-07-02 05:49:55.234 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:55.246 blo = 283, bhi = 1101
2025-07-02 05:49:55.256
2025-07-02 05:49:55.266 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:55.279 g = []
2025-07-02 05:49:55.286 if alo < ahi:
2025-07-02 05:49:55.293 if blo < bhi:
2025-07-02 05:49:55.305 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:55.323 else:
2025-07-02 05:49:55.339 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:55.347 elif blo < bhi:
2025-07-02 05:49:55.355 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:55.361
2025-07-02 05:49:55.366 >       yield from g
2025-07-02 05:49:55.371
2025-07-02 05:49:55.377 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:55.385 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:55.391
2025-07-02 05:49:55.398 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:55.404 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:55.413 alo = 283, ahi = 1101
2025-07-02 05:49:55.425 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:55.437 blo = 283, bhi = 1101
2025-07-02 05:49:55.449
2025-07-02 05:49:55.459 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:55.468 r"""
2025-07-02 05:49:55.476 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:55.483 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:55.491 synch point, and intraline difference marking is done on the
2025-07-02 05:49:55.502 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:55.514
2025-07-02 05:49:55.525 Example:
2025-07-02 05:49:55.535
2025-07-02 05:49:55.547 >>> d = Differ()
2025-07-02 05:49:55.559 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:55.575 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:55.585 >>> print(''.join(results), end="")
2025-07-02 05:49:55.592 - abcDefghiJkl
2025-07-02 05:49:55.606 + abcdefGhijkl
2025-07-02 05:49:55.619 """
2025-07-02 05:49:55.625
2025-07-02 05:49:55.635 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:55.647 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:55.656 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:55.667 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:55.677 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:55.689
2025-07-02 05:49:55.697 # search for the pair that matches best without being identical
2025-07-02 05:49:55.704 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:55.710 # on junk -- unless we have to)
2025-07-02 05:49:55.722 for j in range(blo, bhi):
2025-07-02 05:49:55.732 bj = b[j]
2025-07-02 05:49:55.743 cruncher.set_seq2(bj)
2025-07-02 05:49:55.752 for i in range(alo, ahi):
2025-07-02 05:49:55.759 ai = a[i]
2025-07-02 05:49:55.764 if ai == bj:
2025-07-02 05:49:55.774 if eqi is None:
2025-07-02 05:49:55.786 eqi, eqj = i, j
2025-07-02 05:49:55.795 continue
2025-07-02 05:49:55.803 cruncher.set_seq1(ai)
2025-07-02 05:49:55.813 # computing similarity is expensive, so use the quick
2025-07-02 05:49:55.823 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:55.835 # compares by a factor of 3.
2025-07-02 05:49:55.846 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:55.860 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:55.869 # of the computation is cached by cruncher
2025-07-02 05:49:55.880 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:55.889 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:55.897 cruncher.ratio() > best_ratio:
2025-07-02 05:49:55.911 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:55.920 if best_ratio < cutoff:
2025-07-02 05:49:55.927 # no non-identical "pretty close" pair
2025-07-02 05:49:55.939 if eqi is None:
2025-07-02 05:49:55.950 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:55.959 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:55.966 return
2025-07-02 05:49:55.974 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:55.984 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:55.992 else:
2025-07-02 05:49:56.000 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:56.007 eqi = None
2025-07-02 05:49:56.013
2025-07-02 05:49:56.020 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:56.026 # identical
2025-07-02 05:49:56.032
2025-07-02 05:49:56.038 # pump out diffs from before the synch point
2025-07-02 05:49:56.047 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:56.059
2025-07-02 05:49:56.067 # do intraline marking on the synch pair
2025-07-02 05:49:56.074 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:56.087 if eqi is None:
2025-07-02 05:49:56.101 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:56.112 atags = btags = ""
2025-07-02 05:49:56.124 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:56.134 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:56.148 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:56.160 if tag == 'replace':
2025-07-02 05:49:56.170 atags += '^' * la
2025-07-02 05:49:56.183 btags += '^' * lb
2025-07-02 05:49:56.194 elif tag == 'delete':
2025-07-02 05:49:56.206 atags += '-' * la
2025-07-02 05:49:56.215 elif tag == 'insert':
2025-07-02 05:49:56.224 btags += '+' * lb
2025-07-02 05:49:56.231 elif tag == 'equal':
2025-07-02 05:49:56.237 atags += ' ' * la
2025-07-02 05:49:56.243 btags += ' ' * lb
2025-07-02 05:49:56.251 else:
2025-07-02 05:49:56.264 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:56.273 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:56.281 else:
2025-07-02 05:49:56.292 # the synch pair is identical
2025-07-02 05:49:56.302 yield '  ' + aelt
2025-07-02 05:49:56.312
2025-07-02 05:49:56.325 # pump out diffs from after the synch point
2025-07-02 05:49:56.339 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:56.348
2025-07-02 05:49:56.356 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:56.369 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:56.382
2025-07-02 05:49:56.393 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:56.407 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:56.417 alo = 284, ahi = 1101
2025-07-02 05:49:56.431 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:56.443 blo = 284, bhi = 1101
2025-07-02 05:49:56.454
2025-07-02 05:49:56.463 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:56.471 g = []
2025-07-02 05:49:56.478 if alo < ahi:
2025-07-02 05:49:56.484 if blo < bhi:
2025-07-02 05:49:56.490 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:56.496 else:
2025-07-02 05:49:56.508 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:56.520 elif blo < bhi:
2025-07-02 05:49:56.532 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:56.546
2025-07-02 05:49:56.556 >       yield from g
2025-07-02 05:49:56.564
2025-07-02 05:49:56.572 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:56.586 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:56.600
2025-07-02 05:49:56.608 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:56.616 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:56.621 alo = 284, ahi = 1101
2025-07-02 05:49:56.630 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:56.642 blo = 284, bhi = 1101
2025-07-02 05:49:56.656
2025-07-02 05:49:56.667 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:56.675 r"""
2025-07-02 05:49:56.690 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:56.700 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:56.708 synch point, and intraline difference marking is done on the
2025-07-02 05:49:56.715 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:56.721
2025-07-02 05:49:56.727 Example:
2025-07-02 05:49:56.735
2025-07-02 05:49:56.749 >>> d = Differ()
2025-07-02 05:49:56.761 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:56.771 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:56.778 >>> print(''.join(results), end="")
2025-07-02 05:49:56.785 - abcDefghiJkl
2025-07-02 05:49:56.797 + abcdefGhijkl
2025-07-02 05:49:56.817 """
2025-07-02 05:49:56.826
2025-07-02 05:49:56.833 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:56.839 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:56.846 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:56.852 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:56.858 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:56.864
2025-07-02 05:49:56.870 # search for the pair that matches best without being identical
2025-07-02 05:49:56.877 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:56.888 # on junk -- unless we have to)
2025-07-02 05:49:56.898 for j in range(blo, bhi):
2025-07-02 05:49:56.909 bj = b[j]
2025-07-02 05:49:56.917 cruncher.set_seq2(bj)
2025-07-02 05:49:56.930 for i in range(alo, ahi):
2025-07-02 05:49:56.941 ai = a[i]
2025-07-02 05:49:56.949 if ai == bj:
2025-07-02 05:49:56.963 if eqi is None:
2025-07-02 05:49:56.972 eqi, eqj = i, j
2025-07-02 05:49:56.980 continue
2025-07-02 05:49:56.987 cruncher.set_seq1(ai)
2025-07-02 05:49:56.993 # computing similarity is expensive, so use the quick
2025-07-02 05:49:57.000 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:57.007 # compares by a factor of 3.
2025-07-02 05:49:57.014 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:57.020 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:57.026 # of the computation is cached by cruncher
2025-07-02 05:49:57.032 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:57.038 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:57.045 cruncher.ratio() > best_ratio:
2025-07-02 05:49:57.051 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:57.057 if best_ratio < cutoff:
2025-07-02 05:49:57.063 # no non-identical "pretty close" pair
2025-07-02 05:49:57.068 if eqi is None:
2025-07-02 05:49:57.074 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:57.083 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:57.094 return
2025-07-02 05:49:57.108 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:57.123 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:57.132 else:
2025-07-02 05:49:57.142 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:57.151 eqi = None
2025-07-02 05:49:57.159
2025-07-02 05:49:57.167 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:57.180 # identical
2025-07-02 05:49:57.193
2025-07-02 05:49:57.204 # pump out diffs from before the synch point
2025-07-02 05:49:57.211 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:57.217
2025-07-02 05:49:57.224 # do intraline marking on the synch pair
2025-07-02 05:49:57.237 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:57.245 if eqi is None:
2025-07-02 05:49:57.259 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:57.270 atags = btags = ""
2025-07-02 05:49:57.279 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:57.291 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:57.301 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:57.311 if tag == 'replace':
2025-07-02 05:49:57.323 atags += '^' * la
2025-07-02 05:49:57.332 btags += '^' * lb
2025-07-02 05:49:57.340 elif tag == 'delete':
2025-07-02 05:49:57.347 atags += '-' * la
2025-07-02 05:49:57.353 elif tag == 'insert':
2025-07-02 05:49:57.362 btags += '+' * lb
2025-07-02 05:49:57.371 elif tag == 'equal':
2025-07-02 05:49:57.380 atags += ' ' * la
2025-07-02 05:49:57.387 btags += ' ' * lb
2025-07-02 05:49:57.397 else:
2025-07-02 05:49:57.404 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:57.411 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:57.417 else:
2025-07-02 05:49:57.424 # the synch pair is identical
2025-07-02 05:49:57.431 yield '  ' + aelt
2025-07-02 05:49:57.441
2025-07-02 05:49:57.449 # pump out diffs from after the synch point
2025-07-02 05:49:57.457 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:57.463
2025-07-02 05:49:57.474 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:57.488 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:57.501
2025-07-02 05:49:57.509 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:57.519 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:57.534 alo = 285, ahi = 1101
2025-07-02 05:49:57.549 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:57.558 blo = 285, bhi = 1101
2025-07-02 05:49:57.570
2025-07-02 05:49:57.577 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:57.585 g = []
2025-07-02 05:49:57.591 if alo < ahi:
2025-07-02 05:49:57.596 if blo < bhi:
2025-07-02 05:49:57.601 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:57.606 else:
2025-07-02 05:49:57.612 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:57.619 elif blo < bhi:
2025-07-02 05:49:57.627 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:57.637
2025-07-02 05:49:57.650 >       yield from g
2025-07-02 05:49:57.662
2025-07-02 05:49:57.672 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:57.680 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:57.686
2025-07-02 05:49:57.693 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:57.699 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:57.706 alo = 285, ahi = 1101
2025-07-02 05:49:57.713 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:57.720 blo = 285, bhi = 1101
2025-07-02 05:49:57.728
2025-07-02 05:49:57.735 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:57.742 r"""
2025-07-02 05:49:57.747 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:57.752 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:57.759 synch point, and intraline difference marking is done on the
2025-07-02 05:49:57.768 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:57.777
2025-07-02 05:49:57.783 Example:
2025-07-02 05:49:57.791
2025-07-02 05:49:57.796 >>> d = Differ()
2025-07-02 05:49:57.801 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:57.805 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:57.810 >>> print(''.join(results), end="")
2025-07-02 05:49:57.815 - abcDefghiJkl
2025-07-02 05:49:57.824 + abcdefGhijkl
2025-07-02 05:49:57.834 """
2025-07-02 05:49:57.849
2025-07-02 05:49:57.860 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:57.869 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:57.883 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:57.893 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:57.904 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:57.918
2025-07-02 05:49:57.927 # search for the pair that matches best without being identical
2025-07-02 05:49:57.935 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:57.943 # on junk -- unless we have to)
2025-07-02 05:49:57.954 for j in range(blo, bhi):
2025-07-02 05:49:57.966 bj = b[j]
2025-07-02 05:49:57.976 cruncher.set_seq2(bj)
2025-07-02 05:49:57.983 for i in range(alo, ahi):
2025-07-02 05:49:57.990 ai = a[i]
2025-07-02 05:49:58.003 if ai == bj:
2025-07-02 05:49:58.016 if eqi is None:
2025-07-02 05:49:58.028 eqi, eqj = i, j
2025-07-02 05:49:58.039 continue
2025-07-02 05:49:58.048 cruncher.set_seq1(ai)
2025-07-02 05:49:58.060 # computing similarity is expensive, so use the quick
2025-07-02 05:49:58.072 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:58.081 # compares by a factor of 3.
2025-07-02 05:49:58.091 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:58.104 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:58.113 # of the computation is cached by cruncher
2025-07-02 05:49:58.123 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:58.136 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:58.145 cruncher.ratio() > best_ratio:
2025-07-02 05:49:58.155 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:58.171 if best_ratio < cutoff:
2025-07-02 05:49:58.184 # no non-identical "pretty close" pair
2025-07-02 05:49:58.192 if eqi is None:
2025-07-02 05:49:58.200 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:58.206 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:58.212 return
2025-07-02 05:49:58.217 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:58.224 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:58.230 else:
2025-07-02 05:49:58.236 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:58.242 eqi = None
2025-07-02 05:49:58.248
2025-07-02 05:49:58.253 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:58.257 # identical
2025-07-02 05:49:58.262
2025-07-02 05:49:58.268 # pump out diffs from before the synch point
2025-07-02 05:49:58.273 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:58.277
2025-07-02 05:49:58.284 # do intraline marking on the synch pair
2025-07-02 05:49:58.290 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:58.297 if eqi is None:
2025-07-02 05:49:58.303 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:58.310 atags = btags = ""
2025-07-02 05:49:58.321 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:58.332 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:58.342 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:58.351 if tag == 'replace':
2025-07-02 05:49:58.358 atags += '^' * la
2025-07-02 05:49:58.364 btags += '^' * lb
2025-07-02 05:49:58.370 elif tag == 'delete':
2025-07-02 05:49:58.377 atags += '-' * la
2025-07-02 05:49:58.384 elif tag == 'insert':
2025-07-02 05:49:58.391 btags += '+' * lb
2025-07-02 05:49:58.398 elif tag == 'equal':
2025-07-02 05:49:58.407 atags += ' ' * la
2025-07-02 05:49:58.414 btags += ' ' * lb
2025-07-02 05:49:58.420 else:
2025-07-02 05:49:58.428 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:58.437 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:58.443 else:
2025-07-02 05:49:58.449 # the synch pair is identical
2025-07-02 05:49:58.459 yield '  ' + aelt
2025-07-02 05:49:58.469
2025-07-02 05:49:58.477 # pump out diffs from after the synch point
2025-07-02 05:49:58.484 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:58.490
2025-07-02 05:49:58.496 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:58.505 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:58.521
2025-07-02 05:49:58.531 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:58.539 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:58.546 alo = 286, ahi = 1101
2025-07-02 05:49:58.555 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:58.566 blo = 286, bhi = 1101
2025-07-02 05:49:58.574
2025-07-02 05:49:58.581 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:58.586 g = []
2025-07-02 05:49:58.593 if alo < ahi:
2025-07-02 05:49:58.599 if blo < bhi:
2025-07-02 05:49:58.605 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:58.612 else:
2025-07-02 05:49:58.618 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:58.626 elif blo < bhi:
2025-07-02 05:49:58.636 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:58.643
2025-07-02 05:49:58.651 >       yield from g
2025-07-02 05:49:58.658
2025-07-02 05:49:58.670 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:58.680 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:58.687
2025-07-02 05:49:58.694 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:58.700 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:58.707 alo = 286, ahi = 1101
2025-07-02 05:49:58.716 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:58.723 blo = 286, bhi = 1101
2025-07-02 05:49:58.731
2025-07-02 05:49:58.740 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:58.751 r"""
2025-07-02 05:49:58.760 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:58.767 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:58.774 synch point, and intraline difference marking is done on the
2025-07-02 05:49:58.781 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:58.788
2025-07-02 05:49:58.795 Example:
2025-07-02 05:49:58.803
2025-07-02 05:49:58.810 >>> d = Differ()
2025-07-02 05:49:58.820 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:58.833 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:58.841 >>> print(''.join(results), end="")
2025-07-02 05:49:58.848 - abcDefghiJkl
2025-07-02 05:49:58.860 + abcdefGhijkl
2025-07-02 05:49:58.873 """
2025-07-02 05:49:58.879
2025-07-02 05:49:58.885 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:58.892 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:58.898 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:58.908 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:58.918 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:49:58.926
2025-07-02 05:49:58.932 # search for the pair that matches best without being identical
2025-07-02 05:49:58.940 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:49:58.947 # on junk -- unless we have to)
2025-07-02 05:49:58.955 for j in range(blo, bhi):
2025-07-02 05:49:58.962 bj = b[j]
2025-07-02 05:49:58.974 cruncher.set_seq2(bj)
2025-07-02 05:49:58.985 for i in range(alo, ahi):
2025-07-02 05:49:58.992 ai = a[i]
2025-07-02 05:49:58.998 if ai == bj:
2025-07-02 05:49:59.004 if eqi is None:
2025-07-02 05:49:59.011 eqi, eqj = i, j
2025-07-02 05:49:59.017 continue
2025-07-02 05:49:59.022 cruncher.set_seq1(ai)
2025-07-02 05:49:59.030 # computing similarity is expensive, so use the quick
2025-07-02 05:49:59.039 # upper bounds first -- have seen this speed up messy
2025-07-02 05:49:59.046 # compares by a factor of 3.
2025-07-02 05:49:59.057 # note that ratio() is only expensive to compute the first
2025-07-02 05:49:59.065 # time it's called on a sequence pair; the expensive part
2025-07-02 05:49:59.072 # of the computation is cached by cruncher
2025-07-02 05:49:59.079 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:49:59.085 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:49:59.093 cruncher.ratio() > best_ratio:
2025-07-02 05:49:59.104 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:49:59.116 if best_ratio < cutoff:
2025-07-02 05:49:59.127 # no non-identical "pretty close" pair
2025-07-02 05:49:59.134 if eqi is None:
2025-07-02 05:49:59.143 # no identical pair either -- treat it as a straight replace
2025-07-02 05:49:59.155 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:59.166 return
2025-07-02 05:49:59.179 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:49:59.187 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:49:59.195 else:
2025-07-02 05:49:59.205 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:49:59.214 eqi = None
2025-07-02 05:49:59.221
2025-07-02 05:49:59.234 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:49:59.244 # identical
2025-07-02 05:49:59.252
2025-07-02 05:49:59.260 # pump out diffs from before the synch point
2025-07-02 05:49:59.266 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:49:59.272
2025-07-02 05:49:59.278 # do intraline marking on the synch pair
2025-07-02 05:49:59.284 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:49:59.290 if eqi is None:
2025-07-02 05:49:59.295 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:49:59.303 atags = btags = ""
2025-07-02 05:49:59.314 cruncher.set_seqs(aelt, belt)
2025-07-02 05:49:59.324 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:49:59.331 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:49:59.339 if tag == 'replace':
2025-07-02 05:49:59.350 atags += '^' * la
2025-07-02 05:49:59.359 btags += '^' * lb
2025-07-02 05:49:59.366 elif tag == 'delete':
2025-07-02 05:49:59.378 atags += '-' * la
2025-07-02 05:49:59.390 elif tag == 'insert':
2025-07-02 05:49:59.399 btags += '+' * lb
2025-07-02 05:49:59.407 elif tag == 'equal':
2025-07-02 05:49:59.418 atags += ' ' * la
2025-07-02 05:49:59.431 btags += ' ' * lb
2025-07-02 05:49:59.442 else:
2025-07-02 05:49:59.451 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:49:59.463 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:49:59.471 else:
2025-07-02 05:49:59.479 # the synch pair is identical
2025-07-02 05:49:59.486 yield '  ' + aelt
2025-07-02 05:49:59.493
2025-07-02 05:49:59.499 # pump out diffs from after the synch point
2025-07-02 05:49:59.507 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:49:59.517
2025-07-02 05:49:59.529 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:49:59.538 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:59.545
2025-07-02 05:49:59.553 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:59.560 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:59.566 alo = 287, ahi = 1101
2025-07-02 05:49:59.573 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:59.579 blo = 287, bhi = 1101
2025-07-02 05:49:59.585
2025-07-02 05:49:59.591 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:59.597 g = []
2025-07-02 05:49:59.602 if alo < ahi:
2025-07-02 05:49:59.615 if blo < bhi:
2025-07-02 05:49:59.627 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:49:59.638 else:
2025-07-02 05:49:59.651 g = self._dump('-', a, alo, ahi)
2025-07-02 05:49:59.664 elif blo < bhi:
2025-07-02 05:49:59.673 g = self._dump('+', b, blo, bhi)
2025-07-02 05:49:59.679
2025-07-02 05:49:59.685 >       yield from g
2025-07-02 05:49:59.690
2025-07-02 05:49:59.702 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:49:59.714 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:49:59.725
2025-07-02 05:49:59.735 self = <difflib.Differ object at [hex]>
2025-07-02 05:49:59.744 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:49:59.752 alo = 287, ahi = 1101
2025-07-02 05:49:59.760 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:49:59.773 blo = 287, bhi = 1101
2025-07-02 05:49:59.786
2025-07-02 05:49:59.796 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:49:59.804 r"""
2025-07-02 05:49:59.811 When replacing one block of lines with another, search the blocks
2025-07-02 05:49:59.818 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:49:59.828 synch point, and intraline difference marking is done on the
2025-07-02 05:49:59.837 similar pair. Lots of work, but often worth it.
2025-07-02 05:49:59.845
2025-07-02 05:49:59.851 Example:
2025-07-02 05:49:59.864
2025-07-02 05:49:59.875 >>> d = Differ()
2025-07-02 05:49:59.884 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:49:59.892 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:49:59.899 >>> print(''.join(results), end="")
2025-07-02 05:49:59.906 - abcDefghiJkl
2025-07-02 05:49:59.923 + abcdefGhijkl
2025-07-02 05:49:59.938 """
2025-07-02 05:49:59.948
2025-07-02 05:49:59.958 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:49:59.969 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:49:59.979 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:49:59.987 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:49:59.994 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:00.000
2025-07-02 05:50:00.007 # search for the pair that matches best without being identical
2025-07-02 05:50:00.014 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:00.028 # on junk -- unless we have to)
2025-07-02 05:50:00.039 for j in range(blo, bhi):
2025-07-02 05:50:00.048 bj = b[j]
2025-07-02 05:50:00.055 cruncher.set_seq2(bj)
2025-07-02 05:50:00.061 for i in range(alo, ahi):
2025-07-02 05:50:00.067 ai = a[i]
2025-07-02 05:50:00.074 if ai == bj:
2025-07-02 05:50:00.080 if eqi is None:
2025-07-02 05:50:00.085 eqi, eqj = i, j
2025-07-02 05:50:00.091 continue
2025-07-02 05:50:00.097 cruncher.set_seq1(ai)
2025-07-02 05:50:00.102 # computing similarity is expensive, so use the quick
2025-07-02 05:50:00.108 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:00.119 # compares by a factor of 3.
2025-07-02 05:50:00.128 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:00.135 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:00.142 # of the computation is cached by cruncher
2025-07-02 05:50:00.150 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:00.160 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:00.173 cruncher.ratio() > best_ratio:
2025-07-02 05:50:00.183 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:00.191 if best_ratio < cutoff:
2025-07-02 05:50:00.198 # no non-identical "pretty close" pair
2025-07-02 05:50:00.210 if eqi is None:
2025-07-02 05:50:00.224 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:00.233 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:00.245 return
2025-07-02 05:50:00.252 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:00.261 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:00.267 else:
2025-07-02 05:50:00.274 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:00.281 eqi = None
2025-07-02 05:50:00.286
2025-07-02 05:50:00.293 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:00.300 # identical
2025-07-02 05:50:00.306
2025-07-02 05:50:00.312 # pump out diffs from before the synch point
2025-07-02 05:50:00.317 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:00.322
2025-07-02 05:50:00.327 # do intraline marking on the synch pair
2025-07-02 05:50:00.333 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:00.337 if eqi is None:
2025-07-02 05:50:00.343 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:00.353 atags = btags = ""
2025-07-02 05:50:00.366 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:00.373 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:00.379 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:00.387 if tag == 'replace':
2025-07-02 05:50:00.398 atags += '^' * la
2025-07-02 05:50:00.405 btags += '^' * lb
2025-07-02 05:50:00.419 elif tag == 'delete':
2025-07-02 05:50:00.432 atags += '-' * la
2025-07-02 05:50:00.442 elif tag == 'insert':
2025-07-02 05:50:00.450 btags += '+' * lb
2025-07-02 05:50:00.458 elif tag == 'equal':
2025-07-02 05:50:00.466 atags += ' ' * la
2025-07-02 05:50:00.472 btags += ' ' * lb
2025-07-02 05:50:00.478 else:
2025-07-02 05:50:00.484 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:00.490 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:00.496 else:
2025-07-02 05:50:00.508 # the synch pair is identical
2025-07-02 05:50:00.521 yield '  ' + aelt
2025-07-02 05:50:00.534
2025-07-02 05:50:00.544 # pump out diffs from after the synch point
2025-07-02 05:50:00.553 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:00.560
2025-07-02 05:50:00.567 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:00.573 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:00.579
2025-07-02 05:50:00.586 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:00.592 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:00.599 alo = 290, ahi = 1101
2025-07-02 05:50:00.607 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:00.615 blo = 290, bhi = 1101
2025-07-02 05:50:00.623
2025-07-02 05:50:00.636 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:00.645 g = []
2025-07-02 05:50:00.654 if alo < ahi:
2025-07-02 05:50:00.666 if blo < bhi:
2025-07-02 05:50:00.677 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:00.685 else:
2025-07-02 05:50:00.697 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:00.708 elif blo < bhi:
2025-07-02 05:50:00.717 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:00.724
2025-07-02 05:50:00.738 >       yield from g
2025-07-02 05:50:00.751
2025-07-02 05:50:00.765 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:00.774 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:00.781
2025-07-02 05:50:00.788 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:00.801 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:00.810 alo = 290, ahi = 1101
2025-07-02 05:50:00.819 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:00.830 blo = 290, bhi = 1101
2025-07-02 05:50:00.839
2025-07-02 05:50:00.852 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:00.862 r"""
2025-07-02 05:50:00.869 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:00.880 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:00.890 synch point, and intraline difference marking is done on the
2025-07-02 05:50:00.896 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:00.903
2025-07-02 05:50:00.908 Example:
2025-07-02 05:50:00.913
2025-07-02 05:50:00.926 >>> d = Differ()
2025-07-02 05:50:00.934 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:00.945 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:00.956 >>> print(''.join(results), end="")
2025-07-02 05:50:00.968 - abcDefghiJkl
2025-07-02 05:50:00.993 + abcdefGhijkl
2025-07-02 05:50:01.015 """
2025-07-02 05:50:01.023
2025-07-02 05:50:01.030 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:01.037 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:01.044 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:01.050 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:01.055 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:01.060
2025-07-02 05:50:01.066 # search for the pair that matches best without being identical
2025-07-02 05:50:01.072 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:01.078 # on junk -- unless we have to)
2025-07-02 05:50:01.086 for j in range(blo, bhi):
2025-07-02 05:50:01.094 bj = b[j]
2025-07-02 05:50:01.100 cruncher.set_seq2(bj)
2025-07-02 05:50:01.107 for i in range(alo, ahi):
2025-07-02 05:50:01.113 ai = a[i]
2025-07-02 05:50:01.119 if ai == bj:
2025-07-02 05:50:01.129 if eqi is None:
2025-07-02 05:50:01.139 eqi, eqj = i, j
2025-07-02 05:50:01.148 continue
2025-07-02 05:50:01.157 cruncher.set_seq1(ai)
2025-07-02 05:50:01.169 # computing similarity is expensive, so use the quick
2025-07-02 05:50:01.181 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:01.193 # compares by a factor of 3.
2025-07-02 05:50:01.201 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:01.208 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:01.216 # of the computation is cached by cruncher
2025-07-02 05:50:01.222 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:01.229 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:01.242 cruncher.ratio() > best_ratio:
2025-07-02 05:50:01.252 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:01.260 if best_ratio < cutoff:
2025-07-02 05:50:01.267 # no non-identical "pretty close" pair
2025-07-02 05:50:01.272 if eqi is None:
2025-07-02 05:50:01.278 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:01.287 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:01.299 return
2025-07-02 05:50:01.309 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:01.321 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:01.331 else:
2025-07-02 05:50:01.339 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:01.348 eqi = None
2025-07-02 05:50:01.356
2025-07-02 05:50:01.363 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:01.373 # identical
2025-07-02 05:50:01.379
2025-07-02 05:50:01.384 # pump out diffs from before the synch point
2025-07-02 05:50:01.390 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:01.397
2025-07-02 05:50:01.406 # do intraline marking on the synch pair
2025-07-02 05:50:01.415 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:01.423 if eqi is None:
2025-07-02 05:50:01.431 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:01.439 atags = btags = ""
2025-07-02 05:50:01.447 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:01.456 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:01.468 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:01.476 if tag == 'replace':
2025-07-02 05:50:01.484 atags += '^' * la
2025-07-02 05:50:01.491 btags += '^' * lb
2025-07-02 05:50:01.499 elif tag == 'delete':
2025-07-02 05:50:01.507 atags += '-' * la
2025-07-02 05:50:01.515 elif tag == 'insert':
2025-07-02 05:50:01.522 btags += '+' * lb
2025-07-02 05:50:01.533 elif tag == 'equal':
2025-07-02 05:50:01.545 atags += ' ' * la
2025-07-02 05:50:01.559 btags += ' ' * lb
2025-07-02 05:50:01.571 else:
2025-07-02 05:50:01.580 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:01.589 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:01.596 else:
2025-07-02 05:50:01.603 # the synch pair is identical
2025-07-02 05:50:01.615 yield '  ' + aelt
2025-07-02 05:50:01.625
2025-07-02 05:50:01.637 # pump out diffs from after the synch point
2025-07-02 05:50:01.649 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:01.657
2025-07-02 05:50:01.664 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:01.674 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:01.685
2025-07-02 05:50:01.695 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:01.704 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:01.712 alo = 291, ahi = 1101
2025-07-02 05:50:01.719 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:01.725 blo = 291, bhi = 1101
2025-07-02 05:50:01.731
2025-07-02 05:50:01.737 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:01.746 g = []
2025-07-02 05:50:01.754 if alo < ahi:
2025-07-02 05:50:01.762 if blo < bhi:
2025-07-02 05:50:01.771 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:01.779 else:
2025-07-02 05:50:01.786 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:01.792 elif blo < bhi:
2025-07-02 05:50:01.798 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:01.805
2025-07-02 05:50:01.817 >       yield from g
2025-07-02 05:50:01.827
2025-07-02 05:50:01.835 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:01.842 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:01.848
2025-07-02 05:50:01.855 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:01.863 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:01.873 alo = 291, ahi = 1101
2025-07-02 05:50:01.887 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:01.895 blo = 291, bhi = 1101
2025-07-02 05:50:01.902
2025-07-02 05:50:01.909 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:01.915 r"""
2025-07-02 05:50:01.924 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:01.934 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:01.947 synch point, and intraline difference marking is done on the
2025-07-02 05:50:01.959 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:01.969
2025-07-02 05:50:01.981 Example:
2025-07-02 05:50:01.990
2025-07-02 05:50:02.002 >>> d = Differ()
2025-07-02 05:50:02.016 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:02.027 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:02.036 >>> print(''.join(results), end="")
2025-07-02 05:50:02.044 - abcDefghiJkl
2025-07-02 05:50:02.061 + abcdefGhijkl
2025-07-02 05:50:02.077 """
2025-07-02 05:50:02.085
2025-07-02 05:50:02.099 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:02.112 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:02.123 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:02.131 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:02.144 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:02.154
2025-07-02 05:50:02.162 # search for the pair that matches best without being identical
2025-07-02 05:50:02.171 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:02.178 # on junk -- unless we have to)
2025-07-02 05:50:02.188 for j in range(blo, bhi):
2025-07-02 05:50:02.196 bj = b[j]
2025-07-02 05:50:02.203 cruncher.set_seq2(bj)
2025-07-02 05:50:02.211 for i in range(alo, ahi):
2025-07-02 05:50:02.220 ai = a[i]
2025-07-02 05:50:02.227 if ai == bj:
2025-07-02 05:50:02.237 if eqi is None:
2025-07-02 05:50:02.246 eqi, eqj = i, j
2025-07-02 05:50:02.258 continue
2025-07-02 05:50:02.266 cruncher.set_seq1(ai)
2025-07-02 05:50:02.273 # computing similarity is expensive, so use the quick
2025-07-02 05:50:02.281 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:02.288 # compares by a factor of 3.
2025-07-02 05:50:02.295 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:02.302 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:02.308 # of the computation is cached by cruncher
2025-07-02 05:50:02.315 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:02.322 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:02.329 cruncher.ratio() > best_ratio:
2025-07-02 05:50:02.336 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:02.344 if best_ratio < cutoff:
2025-07-02 05:50:02.356 # no non-identical "pretty close" pair
2025-07-02 05:50:02.365 if eqi is None:
2025-07-02 05:50:02.372 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:02.378 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:02.383 return
2025-07-02 05:50:02.389 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:02.394 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:02.400 else:
2025-07-02 05:50:02.405 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:02.410 eqi = None
2025-07-02 05:50:02.416
2025-07-02 05:50:02.425 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:02.437 # identical
2025-07-02 05:50:02.446
2025-07-02 05:50:02.453 # pump out diffs from before the synch point
2025-07-02 05:50:02.461 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:02.469
2025-07-02 05:50:02.477 # do intraline marking on the synch pair
2025-07-02 05:50:02.485 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:02.493 if eqi is None:
2025-07-02 05:50:02.501 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:02.511 atags = btags = ""
2025-07-02 05:50:02.519 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:02.527 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:02.535 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:02.543 if tag == 'replace':
2025-07-02 05:50:02.551 atags += '^' * la
2025-07-02 05:50:02.559 btags += '^' * lb
2025-07-02 05:50:02.568 elif tag == 'delete':
2025-07-02 05:50:02.576 atags += '-' * la
2025-07-02 05:50:02.584 elif tag == 'insert':
2025-07-02 05:50:02.591 btags += '+' * lb
2025-07-02 05:50:02.599 elif tag == 'equal':
2025-07-02 05:50:02.607 atags += ' ' * la
2025-07-02 05:50:02.615 btags += ' ' * lb
2025-07-02 05:50:02.623 else:
2025-07-02 05:50:02.631 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:02.645 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:02.654 else:
2025-07-02 05:50:02.666 # the synch pair is identical
2025-07-02 05:50:02.677 yield '  ' + aelt
2025-07-02 05:50:02.686
2025-07-02 05:50:02.695 # pump out diffs from after the synch point
2025-07-02 05:50:02.707 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:02.716
2025-07-02 05:50:02.723 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:02.729 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:02.735
2025-07-02 05:50:02.741 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:02.751 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:02.759 alo = 292, ahi = 1101
2025-07-02 05:50:02.766 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:02.774 blo = 292, bhi = 1101
2025-07-02 05:50:02.782
2025-07-02 05:50:02.788 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:02.794 g = []
2025-07-02 05:50:02.800 if alo < ahi:
2025-07-02 05:50:02.809 if blo < bhi:
2025-07-02 05:50:02.816 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:02.824 else:
2025-07-02 05:50:02.830 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:02.836 elif blo < bhi:
2025-07-02 05:50:02.843 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:02.849
2025-07-02 05:50:02.856 >       yield from g
2025-07-02 05:50:02.863
2025-07-02 05:50:02.871 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:02.880 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:02.886
2025-07-02 05:50:02.894 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:02.903 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:02.912 alo = 292, ahi = 1101
2025-07-02 05:50:02.922 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:02.928 blo = 292, bhi = 1101
2025-07-02 05:50:02.942
2025-07-02 05:50:02.950 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:02.957 r"""
2025-07-02 05:50:02.964 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:02.971 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:02.982 synch point, and intraline difference marking is done on the
2025-07-02 05:50:02.989 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:02.995
2025-07-02 05:50:03.001 Example:
2025-07-02 05:50:03.006
2025-07-02 05:50:03.012 >>> d = Differ()
2025-07-02 05:50:03.017 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:03.022 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:03.028 >>> print(''.join(results), end="")
2025-07-02 05:50:03.033 - abcDefghiJkl
2025-07-02 05:50:03.043 + abcdefGhijkl
2025-07-02 05:50:03.052 """
2025-07-02 05:50:03.057
2025-07-02 05:50:03.061 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:03.066 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:03.071 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:03.076 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:03.081 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:03.086
2025-07-02 05:50:03.092 # search for the pair that matches best without being identical
2025-07-02 05:50:03.098 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:03.103 # on junk -- unless we have to)
2025-07-02 05:50:03.111 for j in range(blo, bhi):
2025-07-02 05:50:03.121 bj = b[j]
2025-07-02 05:50:03.133 cruncher.set_seq2(bj)
2025-07-02 05:50:03.142 for i in range(alo, ahi):
2025-07-02 05:50:03.155 ai = a[i]
2025-07-02 05:50:03.166 if ai == bj:
2025-07-02 05:50:03.174 if eqi is None:
2025-07-02 05:50:03.183 eqi, eqj = i, j
2025-07-02 05:50:03.195 continue
2025-07-02 05:50:03.205 cruncher.set_seq1(ai)
2025-07-02 05:50:03.213 # computing similarity is expensive, so use the quick
2025-07-02 05:50:03.221 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:03.227 # compares by a factor of 3.
2025-07-02 05:50:03.234 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:03.240 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:03.246 # of the computation is cached by cruncher
2025-07-02 05:50:03.257 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:03.268 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:03.276 cruncher.ratio() > best_ratio:
2025-07-02 05:50:03.285 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:03.291 if best_ratio < cutoff:
2025-07-02 05:50:03.299 # no non-identical "pretty close" pair
2025-07-02 05:50:03.310 if eqi is None:
2025-07-02 05:50:03.319 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:03.328 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:03.341 return
2025-07-02 05:50:03.352 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:03.363 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:03.371 else:
2025-07-02 05:50:03.379 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:03.388 eqi = None
2025-07-02 05:50:03.397
2025-07-02 05:50:03.405 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:03.412 # identical
2025-07-02 05:50:03.425
2025-07-02 05:50:03.438 # pump out diffs from before the synch point
2025-07-02 05:50:03.449 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:03.462
2025-07-02 05:50:03.472 # do intraline marking on the synch pair
2025-07-02 05:50:03.483 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:03.492 if eqi is None:
2025-07-02 05:50:03.500 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:03.515 atags = btags = ""
2025-07-02 05:50:03.525 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:03.538 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:03.547 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:03.554 if tag == 'replace':
2025-07-02 05:50:03.565 atags += '^' * la
2025-07-02 05:50:03.575 btags += '^' * lb
2025-07-02 05:50:03.583 elif tag == 'delete':
2025-07-02 05:50:03.590 atags += '-' * la
2025-07-02 05:50:03.596 elif tag == 'insert':
2025-07-02 05:50:03.603 btags += '+' * lb
2025-07-02 05:50:03.610 elif tag == 'equal':
2025-07-02 05:50:03.619 atags += ' ' * la
2025-07-02 05:50:03.626 btags += ' ' * lb
2025-07-02 05:50:03.638 else:
2025-07-02 05:50:03.648 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:03.661 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:03.670 else:
2025-07-02 05:50:03.678 # the synch pair is identical
2025-07-02 05:50:03.686 yield '  ' + aelt
2025-07-02 05:50:03.693
2025-07-02 05:50:03.700 # pump out diffs from after the synch point
2025-07-02 05:50:03.709 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:03.716
2025-07-02 05:50:03.725 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:03.734 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:03.746
2025-07-02 05:50:03.758 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:03.769 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:03.780 alo = 293, ahi = 1101
2025-07-02 05:50:03.794 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:03.804 blo = 293, bhi = 1101
2025-07-02 05:50:03.813
2025-07-02 05:50:03.826 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:03.837 g = []
2025-07-02 05:50:03.850 if alo < ahi:
2025-07-02 05:50:03.865 if blo < bhi:
2025-07-02 05:50:03.878 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:03.887 else:
2025-07-02 05:50:03.895 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:03.901 elif blo < bhi:
2025-07-02 05:50:03.908 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:03.915
2025-07-02 05:50:03.924 >       yield from g
2025-07-02 05:50:03.944
2025-07-02 05:50:03.954 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:03.962 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:03.970
2025-07-02 05:50:03.977 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:03.983 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:03.988 alo = 293, ahi = 1101
2025-07-02 05:50:03.996 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:04.001 blo = 293, bhi = 1101
2025-07-02 05:50:04.007
2025-07-02 05:50:04.013 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:04.020 r"""
2025-07-02 05:50:04.027 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:04.036 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:04.049 synch point, and intraline difference marking is done on the
2025-07-02 05:50:04.060 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:04.069
2025-07-02 05:50:04.076 Example:
2025-07-02 05:50:04.082
2025-07-02 05:50:04.092 >>> d = Differ()
2025-07-02 05:50:04.104 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:04.114 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:04.123 >>> print(''.join(results), end="")
2025-07-02 05:50:04.129 - abcDefghiJkl
2025-07-02 05:50:04.142 + abcdefGhijkl
2025-07-02 05:50:04.162 """
2025-07-02 05:50:04.168
2025-07-02 05:50:04.174 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:04.181 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:04.187 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:04.193 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:04.199 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:04.206
2025-07-02 05:50:04.217 # search for the pair that matches best without being identical
2025-07-02 05:50:04.225 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:04.234 # on junk -- unless we have to)
2025-07-02 05:50:04.242 for j in range(blo, bhi):
2025-07-02 05:50:04.250 bj = b[j]
2025-07-02 05:50:04.259 cruncher.set_seq2(bj)
2025-07-02 05:50:04.270 for i in range(alo, ahi):
2025-07-02 05:50:04.279 ai = a[i]
2025-07-02 05:50:04.287 if ai == bj:
2025-07-02 05:50:04.293 if eqi is None:
2025-07-02 05:50:04.300 eqi, eqj = i, j
2025-07-02 05:50:04.307 continue
2025-07-02 05:50:04.314 cruncher.set_seq1(ai)
2025-07-02 05:50:04.324 # computing similarity is expensive, so use the quick
2025-07-02 05:50:04.337 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:04.347 # compares by a factor of 3.
2025-07-02 05:50:04.354 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:04.365 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:04.375 # of the computation is cached by cruncher
2025-07-02 05:50:04.384 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:04.391 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:04.397 cruncher.ratio() > best_ratio:
2025-07-02 05:50:04.403 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:04.409 if best_ratio < cutoff:
2025-07-02 05:50:04.414 # no non-identical "pretty close" pair
2025-07-02 05:50:04.419 if eqi is None:
2025-07-02 05:50:04.426 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:04.439 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:04.447 return
2025-07-02 05:50:04.457 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:04.464 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:04.470 else:
2025-07-02 05:50:04.477 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:04.489 eqi = None
2025-07-02 05:50:04.500
2025-07-02 05:50:04.509 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:04.517 # identical
2025-07-02 05:50:04.523
2025-07-02 05:50:04.537 # pump out diffs from before the synch point
2025-07-02 05:50:04.548 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:04.559
2025-07-02 05:50:04.571 # do intraline marking on the synch pair
2025-07-02 05:50:04.581 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:04.593 if eqi is None:
2025-07-02 05:50:04.604 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:04.612 atags = btags = ""
2025-07-02 05:50:04.621 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:04.627 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:04.634 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:04.640 if tag == 'replace':
2025-07-02 05:50:04.646 atags += '^' * la
2025-07-02 05:50:04.653 btags += '^' * lb
2025-07-02 05:50:04.665 elif tag == 'delete':
2025-07-02 05:50:04.679 atags += '-' * la
2025-07-02 05:50:04.690 elif tag == 'insert':
2025-07-02 05:50:04.699 btags += '+' * lb
2025-07-02 05:50:04.706 elif tag == 'equal':
2025-07-02 05:50:04.719 atags += ' ' * la
2025-07-02 05:50:04.732 btags += ' ' * lb
2025-07-02 05:50:04.741 else:
2025-07-02 05:50:04.748 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:04.755 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:04.762 else:
2025-07-02 05:50:04.772 # the synch pair is identical
2025-07-02 05:50:04.783 yield '  ' + aelt
2025-07-02 05:50:04.792
2025-07-02 05:50:04.799 # pump out diffs from after the synch point
2025-07-02 05:50:04.806 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:04.818
2025-07-02 05:50:04.828 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:04.837 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:04.847
2025-07-02 05:50:04.861 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:04.874 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:04.882 alo = 294, ahi = 1101
2025-07-02 05:50:04.895 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:04.904 blo = 294, bhi = 1101
2025-07-02 05:50:04.912
2025-07-02 05:50:04.919 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:04.926 g = []
2025-07-02 05:50:04.934 if alo < ahi:
2025-07-02 05:50:04.942 if blo < bhi:
2025-07-02 05:50:04.951 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:04.962 else:
2025-07-02 05:50:04.975 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:04.984 elif blo < bhi:
2025-07-02 05:50:04.991 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:05.001
2025-07-02 05:50:05.013 >       yield from g
2025-07-02 05:50:05.025
2025-07-02 05:50:05.038 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:05.047 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:05.057
2025-07-02 05:50:05.070 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:05.079 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:05.093 alo = 294, ahi = 1101
2025-07-02 05:50:05.107 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:05.117 blo = 294, bhi = 1101
2025-07-02 05:50:05.127
2025-07-02 05:50:05.138 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:05.148 r"""
2025-07-02 05:50:05.157 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:05.164 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:05.171 synch point, and intraline difference marking is done on the
2025-07-02 05:50:05.179 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:05.190
2025-07-02 05:50:05.201 Example:
2025-07-02 05:50:05.209
2025-07-02 05:50:05.216 >>> d = Differ()
2025-07-02 05:50:05.222 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:05.229 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:05.235 >>> print(''.join(results), end="")
2025-07-02 05:50:05.243 - abcDefghiJkl
2025-07-02 05:50:05.262 + abcdefGhijkl
2025-07-02 05:50:05.280 """
2025-07-02 05:50:05.293
2025-07-02 05:50:05.307 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:05.317 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:05.325 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:05.333 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:05.340 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:05.347
2025-07-02 05:50:05.361 # search for the pair that matches best without being identical
2025-07-02 05:50:05.374 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:05.384 # on junk -- unless we have to)
2025-07-02 05:50:05.393 for j in range(blo, bhi):
2025-07-02 05:50:05.408 bj = b[j]
2025-07-02 05:50:05.421 cruncher.set_seq2(bj)
2025-07-02 05:50:05.432 for i in range(alo, ahi):
2025-07-02 05:50:05.443 ai = a[i]
2025-07-02 05:50:05.453 if ai == bj:
2025-07-02 05:50:05.466 if eqi is None:
2025-07-02 05:50:05.474 eqi, eqj = i, j
2025-07-02 05:50:05.482 continue
2025-07-02 05:50:05.491 cruncher.set_seq1(ai)
2025-07-02 05:50:05.498 # computing similarity is expensive, so use the quick
2025-07-02 05:50:05.506 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:05.515 # compares by a factor of 3.
2025-07-02 05:50:05.524 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:05.536 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:05.547 # of the computation is cached by cruncher
2025-07-02 05:50:05.555 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:05.561 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:05.573 cruncher.ratio() > best_ratio:
2025-07-02 05:50:05.584 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:05.597 if best_ratio < cutoff:
2025-07-02 05:50:05.610 # no non-identical "pretty close" pair
2025-07-02 05:50:05.623 if eqi is None:
2025-07-02 05:50:05.634 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:05.646 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:05.661 return
2025-07-02 05:50:05.674 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:05.687 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:05.695 else:
2025-07-02 05:50:05.703 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:05.709 eqi = None
2025-07-02 05:50:05.715
2025-07-02 05:50:05.727 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:05.738 # identical
2025-07-02 05:50:05.752
2025-07-02 05:50:05.764 # pump out diffs from before the synch point
2025-07-02 05:50:05.773 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:05.781
2025-07-02 05:50:05.788 # do intraline marking on the synch pair
2025-07-02 05:50:05.795 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:05.801 if eqi is None:
2025-07-02 05:50:05.810 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:05.818 atags = btags = ""
2025-07-02 05:50:05.826 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:05.840 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:05.851 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:05.861 if tag == 'replace':
2025-07-02 05:50:05.871 atags += '^' * la
2025-07-02 05:50:05.884 btags += '^' * lb
2025-07-02 05:50:05.894 elif tag == 'delete':
2025-07-02 05:50:05.903 atags += '-' * la
2025-07-02 05:50:05.911 elif tag == 'insert':
2025-07-02 05:50:05.920 btags += '+' * lb
2025-07-02 05:50:05.933 elif tag == 'equal':
2025-07-02 05:50:05.946 atags += ' ' * la
2025-07-02 05:50:05.958 btags += ' ' * lb
2025-07-02 05:50:05.968 else:
2025-07-02 05:50:05.977 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:05.988 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:05.999 else:
2025-07-02 05:50:06.009 # the synch pair is identical
2025-07-02 05:50:06.020 yield '  ' + aelt
2025-07-02 05:50:06.032
2025-07-02 05:50:06.041 # pump out diffs from after the synch point
2025-07-02 05:50:06.048 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:06.055
2025-07-02 05:50:06.061 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:06.067 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:06.073
2025-07-02 05:50:06.079 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:06.084 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:06.089 alo = 295, ahi = 1101
2025-07-02 05:50:06.094 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:06.099 blo = 295, bhi = 1101
2025-07-02 05:50:06.104
2025-07-02 05:50:06.109 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:06.114 g = []
2025-07-02 05:50:06.118 if alo < ahi:
2025-07-02 05:50:06.123 if blo < bhi:
2025-07-02 05:50:06.128 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:06.135 else:
2025-07-02 05:50:06.140 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:06.145 elif blo < bhi:
2025-07-02 05:50:06.149 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:06.154
2025-07-02 05:50:06.159 >       yield from g
2025-07-02 05:50:06.164
2025-07-02 05:50:06.169 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:06.173 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:06.178
2025-07-02 05:50:06.183 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:06.188 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:06.193 alo = 295, ahi = 1101
2025-07-02 05:50:06.199 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:06.205 blo = 295, bhi = 1101
2025-07-02 05:50:06.211
2025-07-02 05:50:06.217 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:06.222 r"""
2025-07-02 05:50:06.229 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:06.236 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:06.242 synch point, and intraline difference marking is done on the
2025-07-02 05:50:06.248 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:06.254
2025-07-02 05:50:06.267 Example:
2025-07-02 05:50:06.277
2025-07-02 05:50:06.285 >>> d = Differ()
2025-07-02 05:50:06.298 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:06.308 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:06.318 >>> print(''.join(results), end="")
2025-07-02 05:50:06.329 - abcDefghiJkl
2025-07-02 05:50:06.353 + abcdefGhijkl
2025-07-02 05:50:06.375 """
2025-07-02 05:50:06.385
2025-07-02 05:50:06.397 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:06.410 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:06.421 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:06.429 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:06.436 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:06.442
2025-07-02 05:50:06.451 # search for the pair that matches best without being identical
2025-07-02 05:50:06.462 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:06.472 # on junk -- unless we have to)
2025-07-02 05:50:06.480 for j in range(blo, bhi):
2025-07-02 05:50:06.492 bj = b[j]
2025-07-02 05:50:06.503 cruncher.set_seq2(bj)
2025-07-02 05:50:06.517 for i in range(alo, ahi):
2025-07-02 05:50:06.529 ai = a[i]
2025-07-02 05:50:06.537 if ai == bj:
2025-07-02 05:50:06.546 if eqi is None:
2025-07-02 05:50:06.553 eqi, eqj = i, j
2025-07-02 05:50:06.559 continue
2025-07-02 05:50:06.565 cruncher.set_seq1(ai)
2025-07-02 05:50:06.575 # computing similarity is expensive, so use the quick
2025-07-02 05:50:06.584 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:06.591 # compares by a factor of 3.
2025-07-02 05:50:06.599 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:06.606 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:06.617 # of the computation is cached by cruncher
2025-07-02 05:50:06.627 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:06.636 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:06.643 cruncher.ratio() > best_ratio:
2025-07-02 05:50:06.651 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:06.663 if best_ratio < cutoff:
2025-07-02 05:50:06.671 # no non-identical "pretty close" pair
2025-07-02 05:50:06.681 if eqi is None:
2025-07-02 05:50:06.691 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:06.704 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:06.712 return
2025-07-02 05:50:06.720 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:06.727 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:06.733 else:
2025-07-02 05:50:06.745 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:06.755 eqi = None
2025-07-02 05:50:06.763
2025-07-02 05:50:06.771 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:06.781 # identical
2025-07-02 05:50:06.797
2025-07-02 05:50:06.806 # pump out diffs from before the synch point
2025-07-02 05:50:06.814 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:06.821
2025-07-02 05:50:06.827 # do intraline marking on the synch pair
2025-07-02 05:50:06.832 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:06.837 if eqi is None:
2025-07-02 05:50:06.842 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:06.847 atags = btags = ""
2025-07-02 05:50:06.852 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:06.856 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:06.862 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:06.875 if tag == 'replace':
2025-07-02 05:50:06.886 atags += '^' * la
2025-07-02 05:50:06.899 btags += '^' * lb
2025-07-02 05:50:06.912 elif tag == 'delete':
2025-07-02 05:50:06.924 atags += '-' * la
2025-07-02 05:50:06.934 elif tag == 'insert':
2025-07-02 05:50:06.946 btags += '+' * lb
2025-07-02 05:50:06.958 elif tag == 'equal':
2025-07-02 05:50:06.969 atags += ' ' * la
2025-07-02 05:50:06.981 btags += ' ' * lb
2025-07-02 05:50:06.992 else:
2025-07-02 05:50:07.001 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:07.009 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:07.015 else:
2025-07-02 05:50:07.022 # the synch pair is identical
2025-07-02 05:50:07.028 yield '  ' + aelt
2025-07-02 05:50:07.035
2025-07-02 05:50:07.043 # pump out diffs from after the synch point
2025-07-02 05:50:07.055 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:07.064
2025-07-02 05:50:07.070 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:07.077 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:07.083
2025-07-02 05:50:07.094 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:07.106 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:07.119 alo = 296, ahi = 1101
2025-07-02 05:50:07.129 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:07.135 blo = 296, bhi = 1101
2025-07-02 05:50:07.142
2025-07-02 05:50:07.149 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:07.155 g = []
2025-07-02 05:50:07.162 if alo < ahi:
2025-07-02 05:50:07.167 if blo < bhi:
2025-07-02 05:50:07.175 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:07.185 else:
2025-07-02 05:50:07.194 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:07.204 elif blo < bhi:
2025-07-02 05:50:07.214 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:07.223
2025-07-02 05:50:07.232 >       yield from g
2025-07-02 05:50:07.240
2025-07-02 05:50:07.247 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:07.254 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:07.259
2025-07-02 05:50:07.266 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:07.275 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:07.285 alo = 296, ahi = 1101
2025-07-02 05:50:07.293 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:07.300 blo = 296, bhi = 1101
2025-07-02 05:50:07.306
2025-07-02 05:50:07.317 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:07.327 r"""
2025-07-02 05:50:07.335 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:07.343 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:07.351 synch point, and intraline difference marking is done on the
2025-07-02 05:50:07.359 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:07.369
2025-07-02 05:50:07.378 Example:
2025-07-02 05:50:07.386
2025-07-02 05:50:07.397 >>> d = Differ()
2025-07-02 05:50:07.406 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:07.416 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:07.424 >>> print(''.join(results), end="")
2025-07-02 05:50:07.432 - abcDefghiJkl
2025-07-02 05:50:07.455 + abcdefGhijkl
2025-07-02 05:50:07.476 """
2025-07-02 05:50:07.484
2025-07-02 05:50:07.491 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:07.498 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:07.504 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:07.510 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:07.516 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:07.522
2025-07-02 05:50:07.534 # search for the pair that matches best without being identical
2025-07-02 05:50:07.545 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:07.553 # on junk -- unless we have to)
2025-07-02 05:50:07.560 for j in range(blo, bhi):
2025-07-02 05:50:07.566 bj = b[j]
2025-07-02 05:50:07.577 cruncher.set_seq2(bj)
2025-07-02 05:50:07.585 for i in range(alo, ahi):
2025-07-02 05:50:07.592 ai = a[i]
2025-07-02 05:50:07.598 if ai == bj:
2025-07-02 05:50:07.607 if eqi is None:
2025-07-02 05:50:07.618 eqi, eqj = i, j
2025-07-02 05:50:07.627 continue
2025-07-02 05:50:07.635 cruncher.set_seq1(ai)
2025-07-02 05:50:07.641 # computing similarity is expensive, so use the quick
2025-07-02 05:50:07.648 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:07.655 # compares by a factor of 3.
2025-07-02 05:50:07.666 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:07.676 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:07.683 # of the computation is cached by cruncher
2025-07-02 05:50:07.690 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:07.696 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:07.702 cruncher.ratio() > best_ratio:
2025-07-02 05:50:07.709 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:07.714 if best_ratio < cutoff:
2025-07-02 05:50:07.721 # no non-identical "pretty close" pair
2025-07-02 05:50:07.727 if eqi is None:
2025-07-02 05:50:07.741 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:07.753 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:07.763 return
2025-07-02 05:50:07.775 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:07.784 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:07.793 else:
2025-07-02 05:50:07.800 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:07.807 eqi = None
2025-07-02 05:50:07.813
2025-07-02 05:50:07.826 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:07.835 # identical
2025-07-02 05:50:07.843
2025-07-02 05:50:07.850 # pump out diffs from before the synch point
2025-07-02 05:50:07.861 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:07.871
2025-07-02 05:50:07.880 # do intraline marking on the synch pair
2025-07-02 05:50:07.888 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:07.895 if eqi is None:
2025-07-02 05:50:07.905 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:07.913 atags = btags = ""
2025-07-02 05:50:07.919 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:07.932 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:07.943 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:07.956 if tag == 'replace':
2025-07-02 05:50:07.968 atags += '^' * la
2025-07-02 05:50:07.978 btags += '^' * lb
2025-07-02 05:50:07.987 elif tag == 'delete':
2025-07-02 05:50:07.999 atags += '-' * la
2025-07-02 05:50:08.010 elif tag == 'insert':
2025-07-02 05:50:08.023 btags += '+' * lb
2025-07-02 05:50:08.033 elif tag == 'equal':
2025-07-02 05:50:08.041 atags += ' ' * la
2025-07-02 05:50:08.051 btags += ' ' * lb
2025-07-02 05:50:08.063 else:
2025-07-02 05:50:08.075 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:08.084 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:08.091 else:
2025-07-02 05:50:08.099 # the synch pair is identical
2025-07-02 05:50:08.107 yield '  ' + aelt
2025-07-02 05:50:08.115
2025-07-02 05:50:08.130 # pump out diffs from after the synch point
2025-07-02 05:50:08.139 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:08.147
2025-07-02 05:50:08.154 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:08.167 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:08.177
2025-07-02 05:50:08.185 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:08.195 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:08.206 alo = 297, ahi = 1101
2025-07-02 05:50:08.214 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:08.222 blo = 297, bhi = 1101
2025-07-02 05:50:08.232
2025-07-02 05:50:08.242 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:08.250 g = []
2025-07-02 05:50:08.261 if alo < ahi:
2025-07-02 05:50:08.274 if blo < bhi:
2025-07-02 05:50:08.285 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:08.296 else:
2025-07-02 05:50:08.306 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:08.317 elif blo < bhi:
2025-07-02 05:50:08.328 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:08.341
2025-07-02 05:50:08.354 >       yield from g
2025-07-02 05:50:08.365
2025-07-02 05:50:08.375 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:08.387 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:08.396
2025-07-02 05:50:08.404 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:08.417 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:08.427 alo = 297, ahi = 1101
2025-07-02 05:50:08.437 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:08.444 blo = 297, bhi = 1101
2025-07-02 05:50:08.450
2025-07-02 05:50:08.461 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:08.471 r"""
2025-07-02 05:50:08.479 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:08.486 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:08.495 synch point, and intraline difference marking is done on the
2025-07-02 05:50:08.506 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:08.519
2025-07-02 05:50:08.527 Example:
2025-07-02 05:50:08.538
2025-07-02 05:50:08.547 >>> d = Differ()
2025-07-02 05:50:08.555 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:08.561 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:08.566 >>> print(''.join(results), end="")
2025-07-02 05:50:08.572 - abcDefghiJkl
2025-07-02 05:50:08.589 + abcdefGhijkl
2025-07-02 05:50:08.611 """
2025-07-02 05:50:08.622
2025-07-02 05:50:08.633 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:08.645 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:08.656 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:08.667 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:08.676 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:08.683
2025-07-02 05:50:08.690 # search for the pair that matches best without being identical
2025-07-02 05:50:08.697 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:08.704 # on junk -- unless we have to)
2025-07-02 05:50:08.710 for j in range(blo, bhi):
2025-07-02 05:50:08.716 bj = b[j]
2025-07-02 05:50:08.722 cruncher.set_seq2(bj)
2025-07-02 05:50:08.729 for i in range(alo, ahi):
2025-07-02 05:50:08.736 ai = a[i]
2025-07-02 05:50:08.743 if ai == bj:
2025-07-02 05:50:08.758 if eqi is None:
2025-07-02 05:50:08.768 eqi, eqj = i, j
2025-07-02 05:50:08.774 continue
2025-07-02 05:50:08.781 cruncher.set_seq1(ai)
2025-07-02 05:50:08.794 # computing similarity is expensive, so use the quick
2025-07-02 05:50:08.804 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:08.814 # compares by a factor of 3.
2025-07-02 05:50:08.825 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:08.838 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:08.849 # of the computation is cached by cruncher
2025-07-02 05:50:08.858 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:08.868 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:08.879 cruncher.ratio() > best_ratio:
2025-07-02 05:50:08.888 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:08.896 if best_ratio < cutoff:
2025-07-02 05:50:08.903 # no non-identical "pretty close" pair
2025-07-02 05:50:08.912 if eqi is None:
2025-07-02 05:50:08.922 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:08.931 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:08.938 return
2025-07-02 05:50:08.949 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:08.957 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:08.964 else:
2025-07-02 05:50:08.971 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:08.978 eqi = None
2025-07-02 05:50:08.987
2025-07-02 05:50:08.996 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:09.007 # identical
2025-07-02 05:50:09.016
2025-07-02 05:50:09.024 # pump out diffs from before the synch point
2025-07-02 05:50:09.038 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:09.048
2025-07-02 05:50:09.057 # do intraline marking on the synch pair
2025-07-02 05:50:09.064 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:09.070 if eqi is None:
2025-07-02 05:50:09.077 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:09.083 atags = btags = ""
2025-07-02 05:50:09.089 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:09.095 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:09.101 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:09.108 if tag == 'replace':
2025-07-02 05:50:09.114 atags += '^' * la
2025-07-02 05:50:09.123 btags += '^' * lb
2025-07-02 05:50:09.135 elif tag == 'delete':
2025-07-02 05:50:09.144 atags += '-' * la
2025-07-02 05:50:09.154 elif tag == 'insert':
2025-07-02 05:50:09.162 btags += '+' * lb
2025-07-02 05:50:09.172 elif tag == 'equal':
2025-07-02 05:50:09.186 atags += ' ' * la
2025-07-02 05:50:09.195 btags += ' ' * lb
2025-07-02 05:50:09.201 else:
2025-07-02 05:50:09.213 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:09.224 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:09.232 else:
2025-07-02 05:50:09.240 # the synch pair is identical
2025-07-02 05:50:09.247 yield '  ' + aelt
2025-07-02 05:50:09.261
2025-07-02 05:50:09.272 # pump out diffs from after the synch point
2025-07-02 05:50:09.283 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:09.296
2025-07-02 05:50:09.308 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:09.318 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:09.327
2025-07-02 05:50:09.339 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:09.348 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:09.355 alo = 298, ahi = 1101
2025-07-02 05:50:09.368 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:09.376 blo = 298, bhi = 1101
2025-07-02 05:50:09.382
2025-07-02 05:50:09.388 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:09.395 g = []
2025-07-02 05:50:09.400 if alo < ahi:
2025-07-02 05:50:09.406 if blo < bhi:
2025-07-02 05:50:09.412 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:09.417 else:
2025-07-02 05:50:09.426 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:09.433 elif blo < bhi:
2025-07-02 05:50:09.439 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:09.446
2025-07-02 05:50:09.452 >       yield from g
2025-07-02 05:50:09.459
2025-07-02 05:50:09.467 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:09.475 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:09.480
2025-07-02 05:50:09.486 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:09.493 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:09.499 alo = 298, ahi = 1101
2025-07-02 05:50:09.507 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:09.514 blo = 298, bhi = 1101
2025-07-02 05:50:09.521
2025-07-02 05:50:09.528 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:09.544 r"""
2025-07-02 05:50:09.553 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:09.563 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:09.575 synch point, and intraline difference marking is done on the
2025-07-02 05:50:09.587 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:09.601
2025-07-02 05:50:09.613 Example:
2025-07-02 05:50:09.621
2025-07-02 05:50:09.633 >>> d = Differ()
2025-07-02 05:50:09.644 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:09.661 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:09.670 >>> print(''.join(results), end="")
2025-07-02 05:50:09.680 - abcDefghiJkl
2025-07-02 05:50:09.695 + abcdefGhijkl
2025-07-02 05:50:09.708 """
2025-07-02 05:50:09.714
2025-07-02 05:50:09.723 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:09.735 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:09.746 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:09.757 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:09.767 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:09.779
2025-07-02 05:50:09.790 # search for the pair that matches best without being identical
2025-07-02 05:50:09.799 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:09.811 # on junk -- unless we have to)
2025-07-02 05:50:09.823 for j in range(blo, bhi):
2025-07-02 05:50:09.834 bj = b[j]
2025-07-02 05:50:09.843 cruncher.set_seq2(bj)
2025-07-02 05:50:09.850 for i in range(alo, ahi):
2025-07-02 05:50:09.859 ai = a[i]
2025-07-02 05:50:09.870 if ai == bj:
2025-07-02 05:50:09.878 if eqi is None:
2025-07-02 05:50:09.886 eqi, eqj = i, j
2025-07-02 05:50:09.898 continue
2025-07-02 05:50:09.909 cruncher.set_seq1(ai)
2025-07-02 05:50:09.922 # computing similarity is expensive, so use the quick
2025-07-02 05:50:09.932 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:09.940 # compares by a factor of 3.
2025-07-02 05:50:09.947 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:09.954 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:09.966 # of the computation is cached by cruncher
2025-07-02 05:50:09.979 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:09.990 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:10.000 cruncher.ratio() > best_ratio:
2025-07-02 05:50:10.008 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:10.015 if best_ratio < cutoff:
2025-07-02 05:50:10.023 # no non-identical "pretty close" pair
2025-07-02 05:50:10.035 if eqi is None:
2025-07-02 05:50:10.046 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:10.056 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:10.064 return
2025-07-02 05:50:10.070 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:10.076 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:10.083 else:
2025-07-02 05:50:10.089 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:10.095 eqi = None
2025-07-02 05:50:10.101
2025-07-02 05:50:10.107 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:10.113 # identical
2025-07-02 05:50:10.119
2025-07-02 05:50:10.125 # pump out diffs from before the synch point
2025-07-02 05:50:10.131 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:10.138
2025-07-02 05:50:10.152 # do intraline marking on the synch pair
2025-07-02 05:50:10.164 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:10.178 if eqi is None:
2025-07-02 05:50:10.188 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:10.196 atags = btags = ""
2025-07-02 05:50:10.203 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:10.211 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:10.223 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:10.231 if tag == 'replace':
2025-07-02 05:50:10.238 atags += '^' * la
2025-07-02 05:50:10.245 btags += '^' * lb
2025-07-02 05:50:10.251 elif tag == 'delete':
2025-07-02 05:50:10.257 atags += '-' * la
2025-07-02 05:50:10.264 elif tag == 'insert':
2025-07-02 05:50:10.271 btags += '+' * lb
2025-07-02 05:50:10.284 elif tag == 'equal':
2025-07-02 05:50:10.293 atags += ' ' * la
2025-07-02 05:50:10.300 btags += ' ' * lb
2025-07-02 05:50:10.307 else:
2025-07-02 05:50:10.313 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:10.320 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:10.330 else:
2025-07-02 05:50:10.344 # the synch pair is identical
2025-07-02 05:50:10.355 yield '  ' + aelt
2025-07-02 05:50:10.368
2025-07-02 05:50:10.376 # pump out diffs from after the synch point
2025-07-02 05:50:10.382 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:10.394
2025-07-02 05:50:10.404 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:10.415 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:10.425
2025-07-02 05:50:10.434 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:10.443 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:10.450 alo = 299, ahi = 1101
2025-07-02 05:50:10.458 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:10.463 blo = 299, bhi = 1101
2025-07-02 05:50:10.468
2025-07-02 05:50:10.473 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:10.485 g = []
2025-07-02 05:50:10.494 if alo < ahi:
2025-07-02 05:50:10.503 if blo < bhi:
2025-07-02 05:50:10.512 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:10.520 else:
2025-07-02 05:50:10.533 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:10.544 elif blo < bhi:
2025-07-02 05:50:10.554 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:10.563
2025-07-02 05:50:10.571 >       yield from g
2025-07-02 05:50:10.578
2025-07-02 05:50:10.585 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:10.591 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:10.597
2025-07-02 05:50:10.602 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:10.609 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:10.615 alo = 299, ahi = 1101
2025-07-02 05:50:10.623 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:10.631 blo = 299, bhi = 1101
2025-07-02 05:50:10.643
2025-07-02 05:50:10.651 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:10.658 r"""
2025-07-02 05:50:10.667 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:10.680 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:10.691 synch point, and intraline difference marking is done on the
2025-07-02 05:50:10.701 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:10.711
2025-07-02 05:50:10.723 Example:
2025-07-02 05:50:10.734
2025-07-02 05:50:10.748 >>> d = Differ()
2025-07-02 05:50:10.762 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:10.774 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:10.784 >>> print(''.join(results), end="")
2025-07-02 05:50:10.793 - abcDefghiJkl
2025-07-02 05:50:10.806 + abcdefGhijkl
2025-07-02 05:50:10.827 """
2025-07-02 05:50:10.834
2025-07-02 05:50:10.840 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:10.845 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:10.851 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:10.859 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:10.870 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:10.879
2025-07-02 05:50:10.888 # search for the pair that matches best without being identical
2025-07-02 05:50:10.901 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:10.912 # on junk -- unless we have to)
2025-07-02 05:50:10.921 for j in range(blo, bhi):
2025-07-02 05:50:10.933 bj = b[j]
2025-07-02 05:50:10.945 cruncher.set_seq2(bj)
2025-07-02 05:50:10.957 for i in range(alo, ahi):
2025-07-02 05:50:10.969 ai = a[i]
2025-07-02 05:50:10.982 if ai == bj:
2025-07-02 05:50:10.991 if eqi is None:
2025-07-02 05:50:10.998 eqi, eqj = i, j
2025-07-02 05:50:11.008 continue
2025-07-02 05:50:11.020 cruncher.set_seq1(ai)
2025-07-02 05:50:11.033 # computing similarity is expensive, so use the quick
2025-07-02 05:50:11.047 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:11.058 # compares by a factor of 3.
2025-07-02 05:50:11.067 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:11.075 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:11.083 # of the computation is cached by cruncher
2025-07-02 05:50:11.090 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:11.097 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:11.103 cruncher.ratio() > best_ratio:
2025-07-02 05:50:11.110 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:11.116 if best_ratio < cutoff:
2025-07-02 05:50:11.122 # no non-identical "pretty close" pair
2025-07-02 05:50:11.135 if eqi is None:
2025-07-02 05:50:11.144 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:11.152 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:11.159 return
2025-07-02 05:50:11.166 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:11.172 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:11.177 else:
2025-07-02 05:50:11.189 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:11.203 eqi = None
2025-07-02 05:50:11.214
2025-07-02 05:50:11.226 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:11.236 # identical
2025-07-02 05:50:11.245
2025-07-02 05:50:11.251 # pump out diffs from before the synch point
2025-07-02 05:50:11.257 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:11.263
2025-07-02 05:50:11.269 # do intraline marking on the synch pair
2025-07-02 05:50:11.275 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:11.284 if eqi is None:
2025-07-02 05:50:11.293 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:11.301 atags = btags = ""
2025-07-02 05:50:11.308 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:11.315 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:11.320 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:11.325 if tag == 'replace':
2025-07-02 05:50:11.331 atags += '^' * la
2025-07-02 05:50:11.339 btags += '^' * lb
2025-07-02 05:50:11.351 elif tag == 'delete':
2025-07-02 05:50:11.360 atags += '-' * la
2025-07-02 05:50:11.369 elif tag == 'insert':
2025-07-02 05:50:11.380 btags += '+' * lb
2025-07-02 05:50:11.389 elif tag == 'equal':
2025-07-02 05:50:11.400 atags += ' ' * la
2025-07-02 05:50:11.410 btags += ' ' * lb
2025-07-02 05:50:11.417 else:
2025-07-02 05:50:11.424 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:11.430 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:11.436 else:
2025-07-02 05:50:11.443 # the synch pair is identical
2025-07-02 05:50:11.451 yield '  ' + aelt
2025-07-02 05:50:11.463
2025-07-02 05:50:11.472 # pump out diffs from after the synch point
2025-07-02 05:50:11.483 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:11.496
2025-07-02 05:50:11.508 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:11.518 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:11.531
2025-07-02 05:50:11.539 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:11.547 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:11.559 alo = 300, ahi = 1101
2025-07-02 05:50:11.571 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:11.583 blo = 300, bhi = 1101
2025-07-02 05:50:11.593
2025-07-02 05:50:11.603 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:11.614 g = []
2025-07-02 05:50:11.624 if alo < ahi:
2025-07-02 05:50:11.631 if blo < bhi:
2025-07-02 05:50:11.638 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:11.645 else:
2025-07-02 05:50:11.651 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:11.659 elif blo < bhi:
2025-07-02 05:50:11.670 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:11.678
2025-07-02 05:50:11.686 >       yield from g
2025-07-02 05:50:11.695
2025-07-02 05:50:11.703 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:11.711 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:11.721
2025-07-02 05:50:11.734 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:11.745 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:11.753 alo = 300, ahi = 1101
2025-07-02 05:50:11.762 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:11.773 blo = 300, bhi = 1101
2025-07-02 05:50:11.784
2025-07-02 05:50:11.792 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:11.799 r"""
2025-07-02 05:50:11.807 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:11.815 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:11.826 synch point, and intraline difference marking is done on the
2025-07-02 05:50:11.834 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:11.842
2025-07-02 05:50:11.848 Example:
2025-07-02 05:50:11.854
2025-07-02 05:50:11.865 >>> d = Differ()
2025-07-02 05:50:11.876 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:11.885 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:11.897 >>> print(''.join(results), end="")
2025-07-02 05:50:11.907 - abcDefghiJkl
2025-07-02 05:50:11.930 + abcdefGhijkl
2025-07-02 05:50:11.954 """
2025-07-02 05:50:11.965
2025-07-02 05:50:11.974 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:11.984 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:11.995 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:12.005 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:12.017 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:12.029
2025-07-02 05:50:12.039 # search for the pair that matches best without being identical
2025-07-02 05:50:12.048 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:12.054 # on junk -- unless we have to)
2025-07-02 05:50:12.061 for j in range(blo, bhi):
2025-07-02 05:50:12.067 bj = b[j]
2025-07-02 05:50:12.075 cruncher.set_seq2(bj)
2025-07-02 05:50:12.089 for i in range(alo, ahi):
2025-07-02 05:50:12.099 ai = a[i]
2025-07-02 05:50:12.106 if ai == bj:
2025-07-02 05:50:12.115 if eqi is None:
2025-07-02 05:50:12.126 eqi, eqj = i, j
2025-07-02 05:50:12.137 continue
2025-07-02 05:50:12.148 cruncher.set_seq1(ai)
2025-07-02 05:50:12.160 # computing similarity is expensive, so use the quick
2025-07-02 05:50:12.170 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:12.178 # compares by a factor of 3.
2025-07-02 05:50:12.190 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:12.201 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:12.208 # of the computation is cached by cruncher
2025-07-02 05:50:12.215 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:12.223 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:12.231 cruncher.ratio() > best_ratio:
2025-07-02 05:50:12.239 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:12.252 if best_ratio < cutoff:
2025-07-02 05:50:12.264 # no non-identical "pretty close" pair
2025-07-02 05:50:12.275 if eqi is None:
2025-07-02 05:50:12.284 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:12.292 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:12.301 return
2025-07-02 05:50:12.314 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:12.328 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:12.339 else:
2025-07-02 05:50:12.348 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:12.355 eqi = None
2025-07-02 05:50:12.365
2025-07-02 05:50:12.379 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:12.389 # identical
2025-07-02 05:50:12.398
2025-07-02 05:50:12.406 # pump out diffs from before the synch point
2025-07-02 05:50:12.414 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:12.420
2025-07-02 05:50:12.427 # do intraline marking on the synch pair
2025-07-02 05:50:12.435 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:12.446 if eqi is None:
2025-07-02 05:50:12.453 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:12.462 atags = btags = ""
2025-07-02 05:50:12.469 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:12.476 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:12.483 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:12.495 if tag == 'replace':
2025-07-02 05:50:12.505 atags += '^' * la
2025-07-02 05:50:12.513 btags += '^' * lb
2025-07-02 05:50:12.525 elif tag == 'delete':
2025-07-02 05:50:12.537 atags += '-' * la
2025-07-02 05:50:12.548 elif tag == 'insert':
2025-07-02 05:50:12.566 btags += '+' * lb
2025-07-02 05:50:12.578 elif tag == 'equal':
2025-07-02 05:50:12.586 atags += ' ' * la
2025-07-02 05:50:12.592 btags += ' ' * lb
2025-07-02 05:50:12.598 else:
2025-07-02 05:50:12.603 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:12.609 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:12.613 else:
2025-07-02 05:50:12.621 # the synch pair is identical
2025-07-02 05:50:12.628 yield '  ' + aelt
2025-07-02 05:50:12.633
2025-07-02 05:50:12.644 # pump out diffs from after the synch point
2025-07-02 05:50:12.653 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:12.661
2025-07-02 05:50:12.667 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:12.675 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:12.686
2025-07-02 05:50:12.699 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:12.712 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:12.720 alo = 301, ahi = 1101
2025-07-02 05:50:12.729 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:12.736 blo = 301, bhi = 1101
2025-07-02 05:50:12.742
2025-07-02 05:50:12.749 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:12.755 g = []
2025-07-02 05:50:12.764 if alo < ahi:
2025-07-02 05:50:12.774 if blo < bhi:
2025-07-02 05:50:12.783 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:12.790 else:
2025-07-02 05:50:12.804 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:12.815 elif blo < bhi:
2025-07-02 05:50:12.825 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:12.832
2025-07-02 05:50:12.839 >       yield from g
2025-07-02 05:50:12.845
2025-07-02 05:50:12.859 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:12.869 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:12.877
2025-07-02 05:50:12.889 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:12.901 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:12.912 alo = 301, ahi = 1101
2025-07-02 05:50:12.925 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:12.937 blo = 301, bhi = 1101
2025-07-02 05:50:12.946
2025-07-02 05:50:12.956 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:12.967 r"""
2025-07-02 05:50:12.975 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:12.983 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:12.990 synch point, and intraline difference marking is done on the
2025-07-02 05:50:12.997 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:13.003
2025-07-02 05:50:13.008 Example:
2025-07-02 05:50:13.015
2025-07-02 05:50:13.021 >>> d = Differ()
2025-07-02 05:50:13.027 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:13.033 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:13.044 >>> print(''.join(results), end="")
2025-07-02 05:50:13.051 - abcDefghiJkl
2025-07-02 05:50:13.071 + abcdefGhijkl
2025-07-02 05:50:13.092 """
2025-07-02 05:50:13.100
2025-07-02 05:50:13.107 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:13.115 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:13.121 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:13.128 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:13.135 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:13.143
2025-07-02 05:50:13.155 # search for the pair that matches best without being identical
2025-07-02 05:50:13.165 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:13.175 # on junk -- unless we have to)
2025-07-02 05:50:13.186 for j in range(blo, bhi):
2025-07-02 05:50:13.196 bj = b[j]
2025-07-02 05:50:13.206 cruncher.set_seq2(bj)
2025-07-02 05:50:13.218 for i in range(alo, ahi):
2025-07-02 05:50:13.228 ai = a[i]
2025-07-02 05:50:13.235 if ai == bj:
2025-07-02 05:50:13.243 if eqi is None:
2025-07-02 05:50:13.253 eqi, eqj = i, j
2025-07-02 05:50:13.261 continue
2025-07-02 05:50:13.268 cruncher.set_seq1(ai)
2025-07-02 05:50:13.274 # computing similarity is expensive, so use the quick
2025-07-02 05:50:13.281 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:13.288 # compares by a factor of 3.
2025-07-02 05:50:13.296 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:13.302 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:13.309 # of the computation is cached by cruncher
2025-07-02 05:50:13.315 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:13.321 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:13.328 cruncher.ratio() > best_ratio:
2025-07-02 05:50:13.339 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:13.351 if best_ratio < cutoff:
2025-07-02 05:50:13.360 # no non-identical "pretty close" pair
2025-07-02 05:50:13.374 if eqi is None:
2025-07-02 05:50:13.384 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:13.393 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:13.400 return
2025-07-02 05:50:13.411 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:13.423 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:13.432 else:
2025-07-02 05:50:13.446 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:13.457 eqi = None
2025-07-02 05:50:13.467
2025-07-02 05:50:13.478 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:13.490 # identical
2025-07-02 05:50:13.502
2025-07-02 05:50:13.511 # pump out diffs from before the synch point
2025-07-02 05:50:13.520 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:13.527
2025-07-02 05:50:13.535 # do intraline marking on the synch pair
2025-07-02 05:50:13.547 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:13.556 if eqi is None:
2025-07-02 05:50:13.563 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:13.570 atags = btags = ""
2025-07-02 05:50:13.578 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:13.589 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:13.599 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:13.610 if tag == 'replace':
2025-07-02 05:50:13.624 atags += '^' * la
2025-07-02 05:50:13.635 btags += '^' * lb
2025-07-02 05:50:13.647 elif tag == 'delete':
2025-07-02 05:50:13.655 atags += '-' * la
2025-07-02 05:50:13.662 elif tag == 'insert':
2025-07-02 05:50:13.667 btags += '+' * lb
2025-07-02 05:50:13.672 elif tag == 'equal':
2025-07-02 05:50:13.678 atags += ' ' * la
2025-07-02 05:50:13.683 btags += ' ' * lb
2025-07-02 05:50:13.688 else:
2025-07-02 05:50:13.694 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:13.700 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:13.707 else:
2025-07-02 05:50:13.713 # the synch pair is identical
2025-07-02 05:50:13.718 yield '  ' + aelt
2025-07-02 05:50:13.728
2025-07-02 05:50:13.740 # pump out diffs from after the synch point
2025-07-02 05:50:13.752 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:13.762
2025-07-02 05:50:13.775 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:13.788 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:13.798
2025-07-02 05:50:13.810 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:13.820 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:13.829 alo = 302, ahi = 1101
2025-07-02 05:50:13.839 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:13.850 blo = 302, bhi = 1101
2025-07-02 05:50:13.859
2025-07-02 05:50:13.866 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:13.876 g = []
2025-07-02 05:50:13.886 if alo < ahi:
2025-07-02 05:50:13.895 if blo < bhi:
2025-07-02 05:50:13.904 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:13.910 else:
2025-07-02 05:50:13.918 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:13.924 elif blo < bhi:
2025-07-02 05:50:13.933 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:13.943
2025-07-02 05:50:13.951 >       yield from g
2025-07-02 05:50:13.963
2025-07-02 05:50:13.974 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:13.984 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:13.991
2025-07-02 05:50:13.997 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:14.003 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:14.009 alo = 302, ahi = 1101
2025-07-02 05:50:14.015 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:14.021 blo = 302, bhi = 1101
2025-07-02 05:50:14.026
2025-07-02 05:50:14.041 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:14.049 r"""
2025-07-02 05:50:14.055 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:14.067 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:14.080 synch point, and intraline difference marking is done on the
2025-07-02 05:50:14.093 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:14.103
2025-07-02 05:50:14.112 Example:
2025-07-02 05:50:14.120
2025-07-02 05:50:14.127 >>> d = Differ()
2025-07-02 05:50:14.133 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:14.139 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:14.145 >>> print(''.join(results), end="")
2025-07-02 05:50:14.151 - abcDefghiJkl
2025-07-02 05:50:14.163 + abcdefGhijkl
2025-07-02 05:50:14.176 """
2025-07-02 05:50:14.182
2025-07-02 05:50:14.191 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:14.203 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:14.212 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:14.224 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:14.235 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:14.245
2025-07-02 05:50:14.260 # search for the pair that matches best without being identical
2025-07-02 05:50:14.271 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:14.283 # on junk -- unless we have to)
2025-07-02 05:50:14.291 for j in range(blo, bhi):
2025-07-02 05:50:14.298 bj = b[j]
2025-07-02 05:50:14.307 cruncher.set_seq2(bj)
2025-07-02 05:50:14.314 for i in range(alo, ahi):
2025-07-02 05:50:14.321 ai = a[i]
2025-07-02 05:50:14.328 if ai == bj:
2025-07-02 05:50:14.335 if eqi is None:
2025-07-02 05:50:14.341 eqi, eqj = i, j
2025-07-02 05:50:14.347 continue
2025-07-02 05:50:14.354 cruncher.set_seq1(ai)
2025-07-02 05:50:14.366 # computing similarity is expensive, so use the quick
2025-07-02 05:50:14.378 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:14.389 # compares by a factor of 3.
2025-07-02 05:50:14.398 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:14.406 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:14.411 # of the computation is cached by cruncher
2025-07-02 05:50:14.417 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:14.421 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:14.426 cruncher.ratio() > best_ratio:
2025-07-02 05:50:14.431 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:14.435 if best_ratio < cutoff:
2025-07-02 05:50:14.440 # no non-identical "pretty close" pair
2025-07-02 05:50:14.444 if eqi is None:
2025-07-02 05:50:14.449 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:14.454 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:14.458 return
2025-07-02 05:50:14.466 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:14.472 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:14.478 else:
2025-07-02 05:50:14.484 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:14.491 eqi = None
2025-07-02 05:50:14.496
2025-07-02 05:50:14.503 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:14.511 # identical
2025-07-02 05:50:14.521
2025-07-02 05:50:14.531 # pump out diffs from before the synch point
2025-07-02 05:50:14.540 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:14.547
2025-07-02 05:50:14.559 # do intraline marking on the synch pair
2025-07-02 05:50:14.570 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:14.576 if eqi is None:
2025-07-02 05:50:14.583 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:14.589 atags = btags = ""
2025-07-02 05:50:14.593 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:14.598 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:14.603 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:14.608 if tag == 'replace':
2025-07-02 05:50:14.613 atags += '^' * la
2025-07-02 05:50:14.617 btags += '^' * lb
2025-07-02 05:50:14.622 elif tag == 'delete':
2025-07-02 05:50:14.627 atags += '-' * la
2025-07-02 05:50:14.631 elif tag == 'insert':
2025-07-02 05:50:14.636 btags += '+' * lb
2025-07-02 05:50:14.646 elif tag == 'equal':
2025-07-02 05:50:14.653 atags += ' ' * la
2025-07-02 05:50:14.661 btags += ' ' * lb
2025-07-02 05:50:14.667 else:
2025-07-02 05:50:14.676 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:14.691 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:14.705 else:
2025-07-02 05:50:14.718 # the synch pair is identical
2025-07-02 05:50:14.730 yield '  ' + aelt
2025-07-02 05:50:14.743
2025-07-02 05:50:14.755 # pump out diffs from after the synch point
2025-07-02 05:50:14.763 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:14.770
2025-07-02 05:50:14.781 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:14.793 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:14.801
2025-07-02 05:50:14.808 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:14.822 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:14.831 alo = 303, ahi = 1101
2025-07-02 05:50:14.841 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:14.847 blo = 303, bhi = 1101
2025-07-02 05:50:14.856
2025-07-02 05:50:14.867 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:14.877 g = []
2025-07-02 05:50:14.889 if alo < ahi:
2025-07-02 05:50:14.900 if blo < bhi:
2025-07-02 05:50:14.909 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:14.917 else:
2025-07-02 05:50:14.924 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:14.931 elif blo < bhi:
2025-07-02 05:50:14.939 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:14.947
2025-07-02 05:50:14.962 >       yield from g
2025-07-02 05:50:14.972
2025-07-02 05:50:14.983 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:14.992 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:14.999
2025-07-02 05:50:15.006 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:15.015 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:15.025 alo = 303, ahi = 1101
2025-07-02 05:50:15.038 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:15.050 blo = 303, bhi = 1101
2025-07-02 05:50:15.062
2025-07-02 05:50:15.073 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:15.083 r"""
2025-07-02 05:50:15.095 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:15.102 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:15.109 synch point, and intraline difference marking is done on the
2025-07-02 05:50:15.121 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:15.131
2025-07-02 05:50:15.138 Example:
2025-07-02 05:50:15.149
2025-07-02 05:50:15.158 >>> d = Differ()
2025-07-02 05:50:15.172 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:15.185 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:15.194 >>> print(''.join(results), end="")
2025-07-02 05:50:15.200 - abcDefghiJkl
2025-07-02 05:50:15.211 + abcdefGhijkl
2025-07-02 05:50:15.222 """
2025-07-02 05:50:15.227
2025-07-02 05:50:15.234 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:15.245 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:15.253 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:15.259 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:15.267 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:15.277
2025-07-02 05:50:15.290 # search for the pair that matches best without being identical
2025-07-02 05:50:15.300 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:15.312 # on junk -- unless we have to)
2025-07-02 05:50:15.322 for j in range(blo, bhi):
2025-07-02 05:50:15.329 bj = b[j]
2025-07-02 05:50:15.335 cruncher.set_seq2(bj)
2025-07-02 05:50:15.346 for i in range(alo, ahi):
2025-07-02 05:50:15.355 ai = a[i]
2025-07-02 05:50:15.362 if ai == bj:
2025-07-02 05:50:15.374 if eqi is None:
2025-07-02 05:50:15.383 eqi, eqj = i, j
2025-07-02 05:50:15.397 continue
2025-07-02 05:50:15.408 cruncher.set_seq1(ai)
2025-07-02 05:50:15.416 # computing similarity is expensive, so use the quick
2025-07-02 05:50:15.424 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:15.430 # compares by a factor of 3.
2025-07-02 05:50:15.435 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:15.439 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:15.444 # of the computation is cached by cruncher
2025-07-02 05:50:15.449 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:15.454 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:15.464 cruncher.ratio() > best_ratio:
2025-07-02 05:50:15.475 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:15.483 if best_ratio < cutoff:
2025-07-02 05:50:15.490 # no non-identical "pretty close" pair
2025-07-02 05:50:15.497 if eqi is None:
2025-07-02 05:50:15.503 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:15.511 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:15.521 return
2025-07-02 05:50:15.532 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:15.543 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:15.552 else:
2025-07-02 05:50:15.559 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:15.566 eqi = None
2025-07-02 05:50:15.572
2025-07-02 05:50:15.579 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:15.593 # identical
2025-07-02 05:50:15.604
2025-07-02 05:50:15.615 # pump out diffs from before the synch point
2025-07-02 05:50:15.627 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:15.641
2025-07-02 05:50:15.652 # do intraline marking on the synch pair
2025-07-02 05:50:15.661 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:15.668 if eqi is None:
2025-07-02 05:50:15.675 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:15.682 atags = btags = ""
2025-07-02 05:50:15.689 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:15.701 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:15.712 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:15.721 if tag == 'replace':
2025-07-02 05:50:15.728 atags += '^' * la
2025-07-02 05:50:15.737 btags += '^' * lb
2025-07-02 05:50:15.747 elif tag == 'delete':
2025-07-02 05:50:15.757 atags += '-' * la
2025-07-02 05:50:15.767 elif tag == 'insert':
2025-07-02 05:50:15.775 btags += '+' * lb
2025-07-02 05:50:15.791 elif tag == 'equal':
2025-07-02 05:50:15.800 atags += ' ' * la
2025-07-02 05:50:15.806 btags += ' ' * lb
2025-07-02 05:50:15.812 else:
2025-07-02 05:50:15.817 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:15.824 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:15.830 else:
2025-07-02 05:50:15.842 # the synch pair is identical
2025-07-02 05:50:15.849 yield '  ' + aelt
2025-07-02 05:50:15.855
2025-07-02 05:50:15.862 # pump out diffs from after the synch point
2025-07-02 05:50:15.869 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:15.874
2025-07-02 05:50:15.880 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:15.884 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:15.889
2025-07-02 05:50:15.894 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:15.899 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:15.906 alo = 304, ahi = 1101
2025-07-02 05:50:15.914 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:15.923 blo = 304, bhi = 1101
2025-07-02 05:50:15.933
2025-07-02 05:50:15.941 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:15.947 g = []
2025-07-02 05:50:15.955 if alo < ahi:
2025-07-02 05:50:15.965 if blo < bhi:
2025-07-02 05:50:15.974 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:15.980 else:
2025-07-02 05:50:15.987 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:15.996 elif blo < bhi:
2025-07-02 05:50:16.007 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:16.018
2025-07-02 05:50:16.030 >       yield from g
2025-07-02 05:50:16.041
2025-07-02 05:50:16.055 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:16.066 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:16.081
2025-07-02 05:50:16.092 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:16.101 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:16.108 alo = 304, ahi = 1101
2025-07-02 05:50:16.116 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:16.123 blo = 304, bhi = 1101
2025-07-02 05:50:16.129
2025-07-02 05:50:16.136 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:16.143 r"""
2025-07-02 05:50:16.149 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:16.156 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:16.165 synch point, and intraline difference marking is done on the
2025-07-02 05:50:16.176 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:16.190
2025-07-02 05:50:16.203 Example:
2025-07-02 05:50:16.208
2025-07-02 05:50:16.218 >>> d = Differ()
2025-07-02 05:50:16.229 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:16.240 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:16.249 >>> print(''.join(results), end="")
2025-07-02 05:50:16.260 - abcDefghiJkl
2025-07-02 05:50:16.281 + abcdefGhijkl
2025-07-02 05:50:16.298 """
2025-07-02 05:50:16.310
2025-07-02 05:50:16.321 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:16.333 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:16.346 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:16.355 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:16.362 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:16.371
2025-07-02 05:50:16.380 # search for the pair that matches best without being identical
2025-07-02 05:50:16.389 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:16.402 # on junk -- unless we have to)
2025-07-02 05:50:16.412 for j in range(blo, bhi):
2025-07-02 05:50:16.420 bj = b[j]
2025-07-02 05:50:16.427 cruncher.set_seq2(bj)
2025-07-02 05:50:16.433 for i in range(alo, ahi):
2025-07-02 05:50:16.440 ai = a[i]
2025-07-02 05:50:16.446 if ai == bj:
2025-07-02 05:50:16.457 if eqi is None:
2025-07-02 05:50:16.469 eqi, eqj = i, j
2025-07-02 05:50:16.478 continue
2025-07-02 05:50:16.489 cruncher.set_seq1(ai)
2025-07-02 05:50:16.499 # computing similarity is expensive, so use the quick
2025-07-02 05:50:16.507 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:16.515 # compares by a factor of 3.
2025-07-02 05:50:16.526 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:16.537 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:16.550 # of the computation is cached by cruncher
2025-07-02 05:50:16.560 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:16.569 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:16.580 cruncher.ratio() > best_ratio:
2025-07-02 05:50:16.593 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:16.603 if best_ratio < cutoff:
2025-07-02 05:50:16.612 # no non-identical "pretty close" pair
2025-07-02 05:50:16.620 if eqi is None:
2025-07-02 05:50:16.627 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:16.634 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:16.640 return
2025-07-02 05:50:16.646 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:16.652 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:16.657 else:
2025-07-02 05:50:16.664 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:16.668 eqi = None
2025-07-02 05:50:16.673
2025-07-02 05:50:16.678 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:16.683 # identical
2025-07-02 05:50:16.687
2025-07-02 05:50:16.692 # pump out diffs from before the synch point
2025-07-02 05:50:16.697 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:16.702
2025-07-02 05:50:16.707 # do intraline marking on the synch pair
2025-07-02 05:50:16.711 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:16.716 if eqi is None:
2025-07-02 05:50:16.721 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:16.726 atags = btags = ""
2025-07-02 05:50:16.730 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:16.735 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:16.743 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:16.755 if tag == 'replace':
2025-07-02 05:50:16.768 atags += '^' * la
2025-07-02 05:50:16.782 btags += '^' * lb
2025-07-02 05:50:16.796 elif tag == 'delete':
2025-07-02 05:50:16.807 atags += '-' * la
2025-07-02 05:50:16.816 elif tag == 'insert':
2025-07-02 05:50:16.824 btags += '+' * lb
2025-07-02 05:50:16.837 elif tag == 'equal':
2025-07-02 05:50:16.847 atags += ' ' * la
2025-07-02 05:50:16.855 btags += ' ' * lb
2025-07-02 05:50:16.863 else:
2025-07-02 05:50:16.871 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:16.885 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:16.894 else:
2025-07-02 05:50:16.906 # the synch pair is identical
2025-07-02 05:50:16.919 yield '  ' + aelt
2025-07-02 05:50:16.928
2025-07-02 05:50:16.941 # pump out diffs from after the synch point
2025-07-02 05:50:16.951 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:16.962
2025-07-02 05:50:16.972 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:16.979 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:16.986
2025-07-02 05:50:16.991 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:17.001 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:17.014 alo = 305, ahi = 1101
2025-07-02 05:50:17.024 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:17.036 blo = 305, bhi = 1101
2025-07-02 05:50:17.044
2025-07-02 05:50:17.051 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:17.058 g = []
2025-07-02 05:50:17.063 if alo < ahi:
2025-07-02 05:50:17.074 if blo < bhi:
2025-07-02 05:50:17.085 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:17.094 else:
2025-07-02 05:50:17.106 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:17.116 elif blo < bhi:
2025-07-02 05:50:17.127 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:17.134
2025-07-02 05:50:17.145 >       yield from g
2025-07-02 05:50:17.154
2025-07-02 05:50:17.162 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:17.177 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:17.191
2025-07-02 05:50:17.202 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:17.214 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:17.228 alo = 305, ahi = 1101
2025-07-02 05:50:17.240 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:17.247 blo = 305, bhi = 1101
2025-07-02 05:50:17.254
2025-07-02 05:50:17.260 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:17.266 r"""
2025-07-02 05:50:17.272 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:17.278 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:17.288 synch point, and intraline difference marking is done on the
2025-07-02 05:50:17.296 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:17.306
2025-07-02 05:50:17.316 Example:
2025-07-02 05:50:17.325
2025-07-02 05:50:17.333 >>> d = Differ()
2025-07-02 05:50:17.341 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:17.348 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:17.355 >>> print(''.join(results), end="")
2025-07-02 05:50:17.364 - abcDefghiJkl
2025-07-02 05:50:17.382 + abcdefGhijkl
2025-07-02 05:50:17.396 """
2025-07-02 05:50:17.406
2025-07-02 05:50:17.418 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:17.427 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:17.435 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:17.442 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:17.451 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:17.462
2025-07-02 05:50:17.478 # search for the pair that matches best without being identical
2025-07-02 05:50:17.491 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:17.502 # on junk -- unless we have to)
2025-07-02 05:50:17.515 for j in range(blo, bhi):
2025-07-02 05:50:17.525 bj = b[j]
2025-07-02 05:50:17.539 cruncher.set_seq2(bj)
2025-07-02 05:50:17.549 for i in range(alo, ahi):
2025-07-02 05:50:17.556 ai = a[i]
2025-07-02 05:50:17.562 if ai == bj:
2025-07-02 05:50:17.573 if eqi is None:
2025-07-02 05:50:17.582 eqi, eqj = i, j
2025-07-02 05:50:17.591 continue
2025-07-02 05:50:17.603 cruncher.set_seq1(ai)
2025-07-02 05:50:17.617 # computing similarity is expensive, so use the quick
2025-07-02 05:50:17.629 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:17.642 # compares by a factor of 3.
2025-07-02 05:50:17.650 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:17.659 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:17.666 # of the computation is cached by cruncher
2025-07-02 05:50:17.672 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:17.679 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:17.687 cruncher.ratio() > best_ratio:
2025-07-02 05:50:17.698 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:17.711 if best_ratio < cutoff:
2025-07-02 05:50:17.722 # no non-identical "pretty close" pair
2025-07-02 05:50:17.735 if eqi is None:
2025-07-02 05:50:17.746 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:17.754 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:17.762 return
2025-07-02 05:50:17.769 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:17.774 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:17.780 else:
2025-07-02 05:50:17.786 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:17.795 eqi = None
2025-07-02 05:50:17.807
2025-07-02 05:50:17.815 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:17.824 # identical
2025-07-02 05:50:17.832
2025-07-02 05:50:17.839 # pump out diffs from before the synch point
2025-07-02 05:50:17.854 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:17.867
2025-07-02 05:50:17.879 # do intraline marking on the synch pair
2025-07-02 05:50:17.892 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:17.903 if eqi is None:
2025-07-02 05:50:17.916 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:17.927 atags = btags = ""
2025-07-02 05:50:17.939 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:17.951 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:17.961 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:17.974 if tag == 'replace':
2025-07-02 05:50:17.985 atags += '^' * la
2025-07-02 05:50:17.995 btags += '^' * lb
2025-07-02 05:50:18.008 elif tag == 'delete':
2025-07-02 05:50:18.018 atags += '-' * la
2025-07-02 05:50:18.027 elif tag == 'insert':
2025-07-02 05:50:18.034 btags += '+' * lb
2025-07-02 05:50:18.045 elif tag == 'equal':
2025-07-02 05:50:18.052 atags += ' ' * la
2025-07-02 05:50:18.058 btags += ' ' * lb
2025-07-02 05:50:18.064 else:
2025-07-02 05:50:18.072 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:18.079 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:18.088 else:
2025-07-02 05:50:18.101 # the synch pair is identical
2025-07-02 05:50:18.111 yield '  ' + aelt
2025-07-02 05:50:18.118
2025-07-02 05:50:18.126 # pump out diffs from after the synch point
2025-07-02 05:50:18.139 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:18.147
2025-07-02 05:50:18.156 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:18.167 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:18.179
2025-07-02 05:50:18.188 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:18.196 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:18.203 alo = 306, ahi = 1101
2025-07-02 05:50:18.210 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:18.221 blo = 306, bhi = 1101
2025-07-02 05:50:18.233
2025-07-02 05:50:18.244 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:18.257 g = []
2025-07-02 05:50:18.268 if alo < ahi:
2025-07-02 05:50:18.279 if blo < bhi:
2025-07-02 05:50:18.291 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:18.300 else:
2025-07-02 05:50:18.309 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:18.322 elif blo < bhi:
2025-07-02 05:50:18.337 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:18.346
2025-07-02 05:50:18.354 >       yield from g
2025-07-02 05:50:18.361
2025-07-02 05:50:18.367 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:18.374 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:18.379
2025-07-02 05:50:18.385 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:18.392 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:18.399 alo = 306, ahi = 1101
2025-07-02 05:50:18.407 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:18.418 blo = 306, bhi = 1101
2025-07-02 05:50:18.429
2025-07-02 05:50:18.439 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:18.447 r"""
2025-07-02 05:50:18.454 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:18.463 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:18.473 synch point, and intraline difference marking is done on the
2025-07-02 05:50:18.481 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:18.487
2025-07-02 05:50:18.494 Example:
2025-07-02 05:50:18.500
2025-07-02 05:50:18.507 >>> d = Differ()
2025-07-02 05:50:18.516 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:18.526 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:18.535 >>> print(''.join(results), end="")
2025-07-02 05:50:18.545 - abcDefghiJkl
2025-07-02 05:50:18.568 + abcdefGhijkl
2025-07-02 05:50:18.587 """
2025-07-02 05:50:18.599
2025-07-02 05:50:18.609 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:18.616 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:18.622 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:18.632 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:18.640 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:18.648
2025-07-02 05:50:18.654 # search for the pair that matches best without being identical
2025-07-02 05:50:18.661 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:18.668 # on junk -- unless we have to)
2025-07-02 05:50:18.675 for j in range(blo, bhi):
2025-07-02 05:50:18.683 bj = b[j]
2025-07-02 05:50:18.696 cruncher.set_seq2(bj)
2025-07-02 05:50:18.707 for i in range(alo, ahi):
2025-07-02 05:50:18.717 ai = a[i]
2025-07-02 05:50:18.725 if ai == bj:
2025-07-02 05:50:18.732 if eqi is None:
2025-07-02 05:50:18.745 eqi, eqj = i, j
2025-07-02 05:50:18.755 continue
2025-07-02 05:50:18.764 cruncher.set_seq1(ai)
2025-07-02 05:50:18.772 # computing similarity is expensive, so use the quick
2025-07-02 05:50:18.779 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:18.786 # compares by a factor of 3.
2025-07-02 05:50:18.792 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:18.799 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:18.805 # of the computation is cached by cruncher
2025-07-02 05:50:18.812 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:18.818 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:18.830 cruncher.ratio() > best_ratio:
2025-07-02 05:50:18.841 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:18.849 if best_ratio < cutoff:
2025-07-02 05:50:18.857 # no non-identical "pretty close" pair
2025-07-02 05:50:18.864 if eqi is None:
2025-07-02 05:50:18.871 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:18.878 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:18.883 return
2025-07-02 05:50:18.893 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:18.906 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:18.919 else:
2025-07-02 05:50:18.929 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:18.935 eqi = None
2025-07-02 05:50:18.943
2025-07-02 05:50:18.950 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:18.961 # identical
2025-07-02 05:50:18.971
2025-07-02 05:50:18.980 # pump out diffs from before the synch point
2025-07-02 05:50:18.987 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:18.994
2025-07-02 05:50:19.005 # do intraline marking on the synch pair
2025-07-02 05:50:19.015 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:19.023 if eqi is None:
2025-07-02 05:50:19.032 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:19.040 atags = btags = ""
2025-07-02 05:50:19.051 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:19.062 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:19.072 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:19.080 if tag == 'replace':
2025-07-02 05:50:19.086 atags += '^' * la
2025-07-02 05:50:19.099 btags += '^' * lb
2025-07-02 05:50:19.109 elif tag == 'delete':
2025-07-02 05:50:19.117 atags += '-' * la
2025-07-02 05:50:19.124 elif tag == 'insert':
2025-07-02 05:50:19.133 btags += '+' * lb
2025-07-02 05:50:19.147 elif tag == 'equal':
2025-07-02 05:50:19.157 atags += ' ' * la
2025-07-02 05:50:19.166 btags += ' ' * lb
2025-07-02 05:50:19.178 else:
2025-07-02 05:50:19.188 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:19.202 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:19.215 else:
2025-07-02 05:50:19.225 # the synch pair is identical
2025-07-02 05:50:19.230 yield '  ' + aelt
2025-07-02 05:50:19.239
2025-07-02 05:50:19.247 # pump out diffs from after the synch point
2025-07-02 05:50:19.256 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:19.263
2025-07-02 05:50:19.271 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:19.278 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:19.282
2025-07-02 05:50:19.291 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:19.299 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:19.306 alo = 307, ahi = 1101
2025-07-02 05:50:19.315 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:19.322 blo = 307, bhi = 1101
2025-07-02 05:50:19.330
2025-07-02 05:50:19.339 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:19.351 g = []
2025-07-02 05:50:19.360 if alo < ahi:
2025-07-02 05:50:19.369 if blo < bhi:
2025-07-02 05:50:19.378 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:19.387 else:
2025-07-02 05:50:19.393 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:19.399 elif blo < bhi:
2025-07-02 05:50:19.405 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:19.415
2025-07-02 05:50:19.428 >       yield from g
2025-07-02 05:50:19.436
2025-07-02 05:50:19.444 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:19.451 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:19.457
2025-07-02 05:50:19.463 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:19.471 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:19.478 alo = 307, ahi = 1101
2025-07-02 05:50:19.487 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:19.493 blo = 307, bhi = 1101
2025-07-02 05:50:19.498
2025-07-02 05:50:19.507 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:19.518 r"""
2025-07-02 05:50:19.526 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:19.532 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:19.538 synch point, and intraline difference marking is done on the
2025-07-02 05:50:19.546 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:19.555
2025-07-02 05:50:19.561 Example:
2025-07-02 05:50:19.568
2025-07-02 05:50:19.574 >>> d = Differ()
2025-07-02 05:50:19.585 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:19.593 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:19.599 >>> print(''.join(results), end="")
2025-07-02 05:50:19.606 - abcDefghiJkl
2025-07-02 05:50:19.615 + abcdefGhijkl
2025-07-02 05:50:19.624 """
2025-07-02 05:50:19.629
2025-07-02 05:50:19.633 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:19.638 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:19.643 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:19.647 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:19.652 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:19.656
2025-07-02 05:50:19.661 # search for the pair that matches best without being identical
2025-07-02 05:50:19.666 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:19.670 # on junk -- unless we have to)
2025-07-02 05:50:19.674 for j in range(blo, bhi):
2025-07-02 05:50:19.681 bj = b[j]
2025-07-02 05:50:19.687 cruncher.set_seq2(bj)
2025-07-02 05:50:19.693 for i in range(alo, ahi):
2025-07-02 05:50:19.698 ai = a[i]
2025-07-02 05:50:19.704 if ai == bj:
2025-07-02 05:50:19.710 if eqi is None:
2025-07-02 05:50:19.716 eqi, eqj = i, j
2025-07-02 05:50:19.725 continue
2025-07-02 05:50:19.738 cruncher.set_seq1(ai)
2025-07-02 05:50:19.749 # computing similarity is expensive, so use the quick
2025-07-02 05:50:19.757 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:19.764 # compares by a factor of 3.
2025-07-02 05:50:19.777 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:19.786 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:19.792 # of the computation is cached by cruncher
2025-07-02 05:50:19.797 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:19.802 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:19.806 cruncher.ratio() > best_ratio:
2025-07-02 05:50:19.811 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:19.815 if best_ratio < cutoff:
2025-07-02 05:50:19.820 # no non-identical "pretty close" pair
2025-07-02 05:50:19.824 if eqi is None:
2025-07-02 05:50:19.829 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:19.833 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:19.838 return
2025-07-02 05:50:19.842 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:19.846 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:19.851 else:
2025-07-02 05:50:19.855 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:19.860 eqi = None
2025-07-02 05:50:19.864
2025-07-02 05:50:19.869 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:19.873 # identical
2025-07-02 05:50:19.878
2025-07-02 05:50:19.882 # pump out diffs from before the synch point
2025-07-02 05:50:19.887 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:19.891
2025-07-02 05:50:19.896 # do intraline marking on the synch pair
2025-07-02 05:50:19.900 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:19.904 if eqi is None:
2025-07-02 05:50:19.909 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:19.914 atags = btags = ""
2025-07-02 05:50:19.918 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:19.923 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:19.927 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:19.931 if tag == 'replace':
2025-07-02 05:50:19.936 atags += '^' * la
2025-07-02 05:50:19.942 btags += '^' * lb
2025-07-02 05:50:19.950 elif tag == 'delete':
2025-07-02 05:50:19.960 atags += '-' * la
2025-07-02 05:50:19.971 elif tag == 'insert':
2025-07-02 05:50:19.982 btags += '+' * lb
2025-07-02 05:50:19.991 elif tag == 'equal':
2025-07-02 05:50:19.999 atags += ' ' * la
2025-07-02 05:50:20.006 btags += ' ' * lb
2025-07-02 05:50:20.019 else:
2025-07-02 05:50:20.031 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:20.042 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:20.052 else:
2025-07-02 05:50:20.062 # the synch pair is identical
2025-07-02 05:50:20.072 yield '  ' + aelt
2025-07-02 05:50:20.078
2025-07-02 05:50:20.085 # pump out diffs from after the synch point
2025-07-02 05:50:20.092 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:20.098
2025-07-02 05:50:20.108 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:20.117 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:20.124
2025-07-02 05:50:20.131 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:20.141 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:20.150 alo = 308, ahi = 1101
2025-07-02 05:50:20.162 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:20.172 blo = 308, bhi = 1101
2025-07-02 05:50:20.186
2025-07-02 05:50:20.197 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:20.206 g = []
2025-07-02 05:50:20.215 if alo < ahi:
2025-07-02 05:50:20.224 if blo < bhi:
2025-07-02 05:50:20.232 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:20.239 else:
2025-07-02 05:50:20.248 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:20.260 elif blo < bhi:
2025-07-02 05:50:20.269 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:20.276
2025-07-02 05:50:20.283 >       yield from g
2025-07-02 05:50:20.290
2025-07-02 05:50:20.300 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:20.313 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:20.322
2025-07-02 05:50:20.335 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:20.345 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:20.357 alo = 308, ahi = 1101
2025-07-02 05:50:20.366 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:20.374 blo = 308, bhi = 1101
2025-07-02 05:50:20.386
2025-07-02 05:50:20.396 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:20.402 r"""
2025-07-02 05:50:20.411 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:20.420 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:20.426 synch point, and intraline difference marking is done on the
2025-07-02 05:50:20.435 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:20.445
2025-07-02 05:50:20.452 Example:
2025-07-02 05:50:20.458
2025-07-02 05:50:20.464 >>> d = Differ()
2025-07-02 05:50:20.470 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:20.476 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:20.484 >>> print(''.join(results), end="")
2025-07-02 05:50:20.492 - abcDefghiJkl
2025-07-02 05:50:20.507 + abcdefGhijkl
2025-07-02 05:50:20.527 """
2025-07-02 05:50:20.535
2025-07-02 05:50:20.545 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:20.555 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:20.567 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:20.579 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:20.590 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:20.599
2025-07-02 05:50:20.607 # search for the pair that matches best without being identical
2025-07-02 05:50:20.614 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:20.626 # on junk -- unless we have to)
2025-07-02 05:50:20.635 for j in range(blo, bhi):
2025-07-02 05:50:20.644 bj = b[j]
2025-07-02 05:50:20.651 cruncher.set_seq2(bj)
2025-07-02 05:50:20.657 for i in range(alo, ahi):
2025-07-02 05:50:20.664 ai = a[i]
2025-07-02 05:50:20.670 if ai == bj:
2025-07-02 05:50:20.681 if eqi is None:
2025-07-02 05:50:20.689 eqi, eqj = i, j
2025-07-02 05:50:20.697 continue
2025-07-02 05:50:20.704 cruncher.set_seq1(ai)
2025-07-02 05:50:20.717 # computing similarity is expensive, so use the quick
2025-07-02 05:50:20.727 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:20.735 # compares by a factor of 3.
2025-07-02 05:50:20.748 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:20.758 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:20.769 # of the computation is cached by cruncher
2025-07-02 05:50:20.780 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:20.788 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:20.800 cruncher.ratio() > best_ratio:
2025-07-02 05:50:20.809 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:20.819 if best_ratio < cutoff:
2025-07-02 05:50:20.832 # no non-identical "pretty close" pair
2025-07-02 05:50:20.842 if eqi is None:
2025-07-02 05:50:20.851 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:20.862 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:20.875 return
2025-07-02 05:50:20.886 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:20.895 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:20.905 else:
2025-07-02 05:50:20.918 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:20.928 eqi = None
2025-07-02 05:50:20.936
2025-07-02 05:50:20.943 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:20.950 # identical
2025-07-02 05:50:20.962
2025-07-02 05:50:20.972 # pump out diffs from before the synch point
2025-07-02 05:50:20.979 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:20.985
2025-07-02 05:50:20.992 # do intraline marking on the synch pair
2025-07-02 05:50:20.998 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:21.004 if eqi is None:
2025-07-02 05:50:21.011 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:21.017 atags = btags = ""
2025-07-02 05:50:21.024 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:21.030 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:21.036 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:21.042 if tag == 'replace':
2025-07-02 05:50:21.048 atags += '^' * la
2025-07-02 05:50:21.054 btags += '^' * lb
2025-07-02 05:50:21.060 elif tag == 'delete':
2025-07-02 05:50:21.067 atags += '-' * la
2025-07-02 05:50:21.078 elif tag == 'insert':
2025-07-02 05:50:21.087 btags += '+' * lb
2025-07-02 05:50:21.095 elif tag == 'equal':
2025-07-02 05:50:21.105 atags += ' ' * la
2025-07-02 05:50:21.117 btags += ' ' * lb
2025-07-02 05:50:21.127 else:
2025-07-02 05:50:21.136 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:21.143 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:21.149 else:
2025-07-02 05:50:21.155 # the synch pair is identical
2025-07-02 05:50:21.167 yield '  ' + aelt
2025-07-02 05:50:21.179
2025-07-02 05:50:21.191 # pump out diffs from after the synch point
2025-07-02 05:50:21.201 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:21.209
2025-07-02 05:50:21.217 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:21.234 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:21.243
2025-07-02 05:50:21.251 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:21.259 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:21.266 alo = 309, ahi = 1101
2025-07-02 05:50:21.273 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:21.278 blo = 309, bhi = 1101
2025-07-02 05:50:21.285
2025-07-02 05:50:21.293 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:21.300 g = []
2025-07-02 05:50:21.306 if alo < ahi:
2025-07-02 05:50:21.312 if blo < bhi:
2025-07-02 05:50:21.317 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:21.321 else:
2025-07-02 05:50:21.326 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:21.332 elif blo < bhi:
2025-07-02 05:50:21.338 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:21.348
2025-07-02 05:50:21.358 >       yield from g
2025-07-02 05:50:21.365
2025-07-02 05:50:21.372 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:21.379 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:21.389
2025-07-02 05:50:21.399 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:21.407 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:21.412 alo = 309, ahi = 1101
2025-07-02 05:50:21.418 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:21.423 blo = 309, bhi = 1101
2025-07-02 05:50:21.427
2025-07-02 05:50:21.432 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:21.437 r"""
2025-07-02 05:50:21.442 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:21.447 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:21.454 synch point, and intraline difference marking is done on the
2025-07-02 05:50:21.461 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:21.468
2025-07-02 05:50:21.475 Example:
2025-07-02 05:50:21.486
2025-07-02 05:50:21.495 >>> d = Differ()
2025-07-02 05:50:21.502 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:21.507 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:21.512 >>> print(''.join(results), end="")
2025-07-02 05:50:21.517 - abcDefghiJkl
2025-07-02 05:50:21.529 + abcdefGhijkl
2025-07-02 05:50:21.543 """
2025-07-02 05:50:21.554
2025-07-02 05:50:21.561 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:21.569 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:21.575 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:21.581 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:21.587 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:21.591
2025-07-02 05:50:21.596 # search for the pair that matches best without being identical
2025-07-02 05:50:21.602 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:21.607 # on junk -- unless we have to)
2025-07-02 05:50:21.612 for j in range(blo, bhi):
2025-07-02 05:50:21.616 bj = b[j]
2025-07-02 05:50:21.621 cruncher.set_seq2(bj)
2025-07-02 05:50:21.626 for i in range(alo, ahi):
2025-07-02 05:50:21.630 ai = a[i]
2025-07-02 05:50:21.636 if ai == bj:
2025-07-02 05:50:21.642 if eqi is None:
2025-07-02 05:50:21.647 eqi, eqj = i, j
2025-07-02 05:50:21.652 continue
2025-07-02 05:50:21.657 cruncher.set_seq1(ai)
2025-07-02 05:50:21.663 # computing similarity is expensive, so use the quick
2025-07-02 05:50:21.669 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:21.674 # compares by a factor of 3.
2025-07-02 05:50:21.681 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:21.687 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:21.695 # of the computation is cached by cruncher
2025-07-02 05:50:21.705 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:21.714 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:21.721 cruncher.ratio() > best_ratio:
2025-07-02 05:50:21.732 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:21.740 if best_ratio < cutoff:
2025-07-02 05:50:21.747 # no non-identical "pretty close" pair
2025-07-02 05:50:21.757 if eqi is None:
2025-07-02 05:50:21.770 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:21.782 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:21.794 return
2025-07-02 05:50:21.804 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:21.816 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:21.827 else:
2025-07-02 05:50:21.835 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:21.844 eqi = None
2025-07-02 05:50:21.854
2025-07-02 05:50:21.863 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:21.872 # identical
2025-07-02 05:50:21.886
2025-07-02 05:50:21.894 # pump out diffs from before the synch point
2025-07-02 05:50:21.901 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:21.908
2025-07-02 05:50:21.916 # do intraline marking on the synch pair
2025-07-02 05:50:21.923 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:21.931 if eqi is None:
2025-07-02 05:50:21.943 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:21.951 atags = btags = ""
2025-07-02 05:50:21.959 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:21.966 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:21.978 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:21.988 if tag == 'replace':
2025-07-02 05:50:21.996 atags += '^' * la
2025-07-02 05:50:22.003 btags += '^' * lb
2025-07-02 05:50:22.012 elif tag == 'delete':
2025-07-02 05:50:22.025 atags += '-' * la
2025-07-02 05:50:22.034 elif tag == 'insert':
2025-07-02 05:50:22.041 btags += '+' * lb
2025-07-02 05:50:22.047 elif tag == 'equal':
2025-07-02 05:50:22.054 atags += ' ' * la
2025-07-02 05:50:22.061 btags += ' ' * lb
2025-07-02 05:50:22.067 else:
2025-07-02 05:50:22.074 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:22.081 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:22.087 else:
2025-07-02 05:50:22.094 # the synch pair is identical
2025-07-02 05:50:22.100 yield '  ' + aelt
2025-07-02 05:50:22.107
2025-07-02 05:50:22.113 # pump out diffs from after the synch point
2025-07-02 05:50:22.119 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:22.124
2025-07-02 05:50:22.128 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:22.133 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:22.138
2025-07-02 05:50:22.144 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:22.151 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:22.156 alo = 312, ahi = 1101
2025-07-02 05:50:22.165 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:22.170 blo = 312, bhi = 1101
2025-07-02 05:50:22.182
2025-07-02 05:50:22.192 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:22.202 g = []
2025-07-02 05:50:22.209 if alo < ahi:
2025-07-02 05:50:22.215 if blo < bhi:
2025-07-02 05:50:22.222 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:22.228 else:
2025-07-02 05:50:22.235 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:22.242 elif blo < bhi:
2025-07-02 05:50:22.248 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:22.254
2025-07-02 05:50:22.260 >       yield from g
2025-07-02 05:50:22.272
2025-07-02 05:50:22.286 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:22.295 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:22.303
2025-07-02 05:50:22.311 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:22.320 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:22.331 alo = 312, ahi = 1101
2025-07-02 05:50:22.342 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:22.352 blo = 312, bhi = 1101
2025-07-02 05:50:22.362
2025-07-02 05:50:22.369 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:22.376 r"""
2025-07-02 05:50:22.383 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:22.389 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:22.395 synch point, and intraline difference marking is done on the
2025-07-02 05:50:22.403 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:22.413
2025-07-02 05:50:22.422 Example:
2025-07-02 05:50:22.429
2025-07-02 05:50:22.435 >>> d = Differ()
2025-07-02 05:50:22.443 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:22.454 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:22.462 >>> print(''.join(results), end="")
2025-07-02 05:50:22.470 - abcDefghiJkl
2025-07-02 05:50:22.491 + abcdefGhijkl
2025-07-02 05:50:22.513 """
2025-07-02 05:50:22.520
2025-07-02 05:50:22.528 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:22.535 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:22.542 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:22.548 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:22.554 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:22.560
2025-07-02 05:50:22.572 # search for the pair that matches best without being identical
2025-07-02 05:50:22.582 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:22.590 # on junk -- unless we have to)
2025-07-02 05:50:22.600 for j in range(blo, bhi):
2025-07-02 05:50:22.610 bj = b[j]
2025-07-02 05:50:22.619 cruncher.set_seq2(bj)
2025-07-02 05:50:22.631 for i in range(alo, ahi):
2025-07-02 05:50:22.642 ai = a[i]
2025-07-02 05:50:22.653 if ai == bj:
2025-07-02 05:50:22.663 if eqi is None:
2025-07-02 05:50:22.670 eqi, eqj = i, j
2025-07-02 05:50:22.684 continue
2025-07-02 05:50:22.696 cruncher.set_seq1(ai)
2025-07-02 05:50:22.705 # computing similarity is expensive, so use the quick
2025-07-02 05:50:22.712 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:22.718 # compares by a factor of 3.
2025-07-02 05:50:22.727 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:22.737 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:22.746 # of the computation is cached by cruncher
2025-07-02 05:50:22.757 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:22.769 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:22.780 cruncher.ratio() > best_ratio:
2025-07-02 05:50:22.793 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:22.805 if best_ratio < cutoff:
2025-07-02 05:50:22.815 # no non-identical "pretty close" pair
2025-07-02 05:50:22.823 if eqi is None:
2025-07-02 05:50:22.830 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:22.837 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:22.842 return
2025-07-02 05:50:22.848 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:22.854 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:22.864 else:
2025-07-02 05:50:22.876 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:22.887 eqi = None
2025-07-02 05:50:22.898
2025-07-02 05:50:22.909 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:22.917 # identical
2025-07-02 05:50:22.924
2025-07-02 05:50:22.930 # pump out diffs from before the synch point
2025-07-02 05:50:22.937 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:22.942
2025-07-02 05:50:22.948 # do intraline marking on the synch pair
2025-07-02 05:50:22.955 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:22.961 if eqi is None:
2025-07-02 05:50:22.973 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:22.982 atags = btags = ""
2025-07-02 05:50:22.996 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:23.007 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:23.019 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:23.028 if tag == 'replace':
2025-07-02 05:50:23.037 atags += '^' * la
2025-07-02 05:50:23.044 btags += '^' * lb
2025-07-02 05:50:23.050 elif tag == 'delete':
2025-07-02 05:50:23.057 atags += '-' * la
2025-07-02 05:50:23.063 elif tag == 'insert':
2025-07-02 05:50:23.073 btags += '+' * lb
2025-07-02 05:50:23.082 elif tag == 'equal':
2025-07-02 05:50:23.089 atags += ' ' * la
2025-07-02 05:50:23.097 btags += ' ' * lb
2025-07-02 05:50:23.104 else:
2025-07-02 05:50:23.110 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:23.117 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:23.123 else:
2025-07-02 05:50:23.129 # the synch pair is identical
2025-07-02 05:50:23.135 yield '  ' + aelt
2025-07-02 05:50:23.141
2025-07-02 05:50:23.147 # pump out diffs from after the synch point
2025-07-02 05:50:23.154 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:23.162
2025-07-02 05:50:23.176 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:23.187 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:23.195
2025-07-02 05:50:23.202 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:23.211 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:23.223 alo = 313, ahi = 1101
2025-07-02 05:50:23.230 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:23.238 blo = 313, bhi = 1101
2025-07-02 05:50:23.250
2025-07-02 05:50:23.259 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:23.269 g = []
2025-07-02 05:50:23.279 if alo < ahi:
2025-07-02 05:50:23.290 if blo < bhi:
2025-07-02 05:50:23.300 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:23.308 else:
2025-07-02 05:50:23.315 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:23.321 elif blo < bhi:
2025-07-02 05:50:23.336 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:23.349
2025-07-02 05:50:23.359 >       yield from g
2025-07-02 05:50:23.372
2025-07-02 05:50:23.382 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:23.395 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:23.406
2025-07-02 05:50:23.420 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:23.435 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:23.448 alo = 313, ahi = 1101
2025-07-02 05:50:23.462 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:23.471 blo = 313, bhi = 1101
2025-07-02 05:50:23.479
2025-07-02 05:50:23.490 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:23.499 r"""
2025-07-02 05:50:23.507 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:23.518 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:23.531 synch point, and intraline difference marking is done on the
2025-07-02 05:50:23.541 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:23.548
2025-07-02 05:50:23.555 Example:
2025-07-02 05:50:23.561
2025-07-02 05:50:23.566 >>> d = Differ()
2025-07-02 05:50:23.572 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:23.585 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:23.600 >>> print(''.join(results), end="")
2025-07-02 05:50:23.610 - abcDefghiJkl
2025-07-02 05:50:23.624 + abcdefGhijkl
2025-07-02 05:50:23.638 """
2025-07-02 05:50:23.648
2025-07-02 05:50:23.657 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:23.669 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:23.679 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:23.692 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:23.702 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:23.714
2025-07-02 05:50:23.725 # search for the pair that matches best without being identical
2025-07-02 05:50:23.735 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:23.747 # on junk -- unless we have to)
2025-07-02 05:50:23.758 for j in range(blo, bhi):
2025-07-02 05:50:23.768 bj = b[j]
2025-07-02 05:50:23.778 cruncher.set_seq2(bj)
2025-07-02 05:50:23.788 for i in range(alo, ahi):
2025-07-02 05:50:23.800 ai = a[i]
2025-07-02 05:50:23.812 if ai == bj:
2025-07-02 05:50:23.824 if eqi is None:
2025-07-02 05:50:23.833 eqi, eqj = i, j
2025-07-02 05:50:23.840 continue
2025-07-02 05:50:23.847 cruncher.set_seq1(ai)
2025-07-02 05:50:23.855 # computing similarity is expensive, so use the quick
2025-07-02 05:50:23.866 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:23.880 # compares by a factor of 3.
2025-07-02 05:50:23.892 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:23.901 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:23.909 # of the computation is cached by cruncher
2025-07-02 05:50:23.915 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:23.922 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:23.928 cruncher.ratio() > best_ratio:
2025-07-02 05:50:23.940 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:23.952 if best_ratio < cutoff:
2025-07-02 05:50:23.965 # no non-identical "pretty close" pair
2025-07-02 05:50:23.975 if eqi is None:
2025-07-02 05:50:23.984 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:23.991 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:23.998 return
2025-07-02 05:50:24.004 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:24.011 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:24.017 else:
2025-07-02 05:50:24.023 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:24.034 eqi = None
2025-07-02 05:50:24.043
2025-07-02 05:50:24.051 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:24.057 # identical
2025-07-02 05:50:24.063
2025-07-02 05:50:24.070 # pump out diffs from before the synch point
2025-07-02 05:50:24.077 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:24.083
2025-07-02 05:50:24.089 # do intraline marking on the synch pair
2025-07-02 05:50:24.095 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:24.101 if eqi is None:
2025-07-02 05:50:24.107 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:24.113 atags = btags = ""
2025-07-02 05:50:24.119 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:24.132 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:24.139 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:24.147 if tag == 'replace':
2025-07-02 05:50:24.155 atags += '^' * la
2025-07-02 05:50:24.168 btags += '^' * lb
2025-07-02 05:50:24.182 elif tag == 'delete':
2025-07-02 05:50:24.190 atags += '-' * la
2025-07-02 05:50:24.197 elif tag == 'insert':
2025-07-02 05:50:24.203 btags += '+' * lb
2025-07-02 05:50:24.209 elif tag == 'equal':
2025-07-02 05:50:24.214 atags += ' ' * la
2025-07-02 05:50:24.219 btags += ' ' * lb
2025-07-02 05:50:24.224 else:
2025-07-02 05:50:24.228 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:24.234 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:24.239 else:
2025-07-02 05:50:24.244 # the synch pair is identical
2025-07-02 05:50:24.250 yield '  ' + aelt
2025-07-02 05:50:24.255
2025-07-02 05:50:24.261 # pump out diffs from after the synch point
2025-07-02 05:50:24.266 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:24.277
2025-07-02 05:50:24.286 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:24.292 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:24.298
2025-07-02 05:50:24.308 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:24.316 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:24.322 alo = 314, ahi = 1101
2025-07-02 05:50:24.332 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:24.344 blo = 314, bhi = 1101
2025-07-02 05:50:24.352
2025-07-02 05:50:24.359 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:24.370 g = []
2025-07-02 05:50:24.380 if alo < ahi:
2025-07-02 05:50:24.387 if blo < bhi:
2025-07-02 05:50:24.394 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:24.399 else:
2025-07-02 05:50:24.405 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:24.410 elif blo < bhi:
2025-07-02 05:50:24.415 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:24.420
2025-07-02 05:50:24.427 >       yield from g
2025-07-02 05:50:24.434
2025-07-02 05:50:24.441 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:24.448 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:24.455
2025-07-02 05:50:24.463 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:24.473 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:24.481 alo = 314, ahi = 1101
2025-07-02 05:50:24.490 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:24.497 blo = 314, bhi = 1101
2025-07-02 05:50:24.509
2025-07-02 05:50:24.519 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:24.527 r"""
2025-07-02 05:50:24.540 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:24.554 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:24.562 synch point, and intraline difference marking is done on the
2025-07-02 05:50:24.569 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:24.577
2025-07-02 05:50:24.584 Example:
2025-07-02 05:50:24.591
2025-07-02 05:50:24.604 >>> d = Differ()
2025-07-02 05:50:24.614 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:24.624 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:24.636 >>> print(''.join(results), end="")
2025-07-02 05:50:24.645 - abcDefghiJkl
2025-07-02 05:50:24.663 + abcdefGhijkl
2025-07-02 05:50:24.678 """
2025-07-02 05:50:24.686
2025-07-02 05:50:24.702 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:24.711 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:24.718 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:24.723 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:24.729 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:24.735
2025-07-02 05:50:24.742 # search for the pair that matches best without being identical
2025-07-02 05:50:24.748 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:24.754 # on junk -- unless we have to)
2025-07-02 05:50:24.761 for j in range(blo, bhi):
2025-07-02 05:50:24.768 bj = b[j]
2025-07-02 05:50:24.774 cruncher.set_seq2(bj)
2025-07-02 05:50:24.781 for i in range(alo, ahi):
2025-07-02 05:50:24.793 ai = a[i]
2025-07-02 05:50:24.806 if ai == bj:
2025-07-02 05:50:24.818 if eqi is None:
2025-07-02 05:50:24.830 eqi, eqj = i, j
2025-07-02 05:50:24.837 continue
2025-07-02 05:50:24.845 cruncher.set_seq1(ai)
2025-07-02 05:50:24.852 # computing similarity is expensive, so use the quick
2025-07-02 05:50:24.860 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:24.867 # compares by a factor of 3.
2025-07-02 05:50:24.875 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:24.883 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:24.891 # of the computation is cached by cruncher
2025-07-02 05:50:24.899 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:24.908 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:24.916 cruncher.ratio() > best_ratio:
2025-07-02 05:50:24.922 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:24.930 if best_ratio < cutoff:
2025-07-02 05:50:24.941 # no non-identical "pretty close" pair
2025-07-02 05:50:24.953 if eqi is None:
2025-07-02 05:50:24.962 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:24.968 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:24.974 return
2025-07-02 05:50:24.979 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:24.985 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:24.992 else:
2025-07-02 05:50:24.999 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:25.007 eqi = None
2025-07-02 05:50:25.020
2025-07-02 05:50:25.030 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:25.039 # identical
2025-07-02 05:50:25.046
2025-07-02 05:50:25.055 # pump out diffs from before the synch point
2025-07-02 05:50:25.064 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:25.071
2025-07-02 05:50:25.076 # do intraline marking on the synch pair
2025-07-02 05:50:25.090 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:25.100 if eqi is None:
2025-07-02 05:50:25.108 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:25.117 atags = btags = ""
2025-07-02 05:50:25.130 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:25.139 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:25.147 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:25.156 if tag == 'replace':
2025-07-02 05:50:25.164 atags += '^' * la
2025-07-02 05:50:25.172 btags += '^' * lb
2025-07-02 05:50:25.179 elif tag == 'delete':
2025-07-02 05:50:25.186 atags += '-' * la
2025-07-02 05:50:25.198 elif tag == 'insert':
2025-07-02 05:50:25.207 btags += '+' * lb
2025-07-02 05:50:25.214 elif tag == 'equal':
2025-07-02 05:50:25.228 atags += ' ' * la
2025-07-02 05:50:25.239 btags += ' ' * lb
2025-07-02 05:50:25.251 else:
2025-07-02 05:50:25.260 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:25.271 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:25.282 else:
2025-07-02 05:50:25.290 # the synch pair is identical
2025-07-02 05:50:25.296 yield '  ' + aelt
2025-07-02 05:50:25.303
2025-07-02 05:50:25.309 # pump out diffs from after the synch point
2025-07-02 05:50:25.315 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:25.321
2025-07-02 05:50:25.331 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:25.338 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:25.344
2025-07-02 05:50:25.350 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:25.358 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:25.368 alo = 315, ahi = 1101
2025-07-02 05:50:25.377 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:25.384 blo = 315, bhi = 1101
2025-07-02 05:50:25.390
2025-07-02 05:50:25.397 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:25.404 g = []
2025-07-02 05:50:25.411 if alo < ahi:
2025-07-02 05:50:25.417 if blo < bhi:
2025-07-02 05:50:25.424 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:25.430 else:
2025-07-02 05:50:25.436 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:25.445 elif blo < bhi:
2025-07-02 05:50:25.452 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:25.458
2025-07-02 05:50:25.464 >       yield from g
2025-07-02 05:50:25.470
2025-07-02 05:50:25.481 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:25.497 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:25.507
2025-07-02 05:50:25.517 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:25.526 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:25.534 alo = 315, ahi = 1101
2025-07-02 05:50:25.542 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:25.549 blo = 315, bhi = 1101
2025-07-02 05:50:25.556
2025-07-02 05:50:25.562 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:25.568 r"""
2025-07-02 05:50:25.577 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:25.589 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:25.600 synch point, and intraline difference marking is done on the
2025-07-02 05:50:25.612 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:25.623
2025-07-02 05:50:25.635 Example:
2025-07-02 05:50:25.645
2025-07-02 05:50:25.655 >>> d = Differ()
2025-07-02 05:50:25.666 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:25.674 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:25.684 >>> print(''.join(results), end="")
2025-07-02 05:50:25.691 - abcDefghiJkl
2025-07-02 05:50:25.711 + abcdefGhijkl
2025-07-02 05:50:25.735 """
2025-07-02 05:50:25.746
2025-07-02 05:50:25.756 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:25.764 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:25.770 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:25.777 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:25.785 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:25.791
2025-07-02 05:50:25.797 # search for the pair that matches best without being identical
2025-07-02 05:50:25.803 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:25.811 # on junk -- unless we have to)
2025-07-02 05:50:25.823 for j in range(blo, bhi):
2025-07-02 05:50:25.832 bj = b[j]
2025-07-02 05:50:25.840 cruncher.set_seq2(bj)
2025-07-02 05:50:25.853 for i in range(alo, ahi):
2025-07-02 05:50:25.864 ai = a[i]
2025-07-02 05:50:25.874 if ai == bj:
2025-07-02 05:50:25.883 if eqi is None:
2025-07-02 05:50:25.896 eqi, eqj = i, j
2025-07-02 05:50:25.907 continue
2025-07-02 05:50:25.915 cruncher.set_seq1(ai)
2025-07-02 05:50:25.923 # computing similarity is expensive, so use the quick
2025-07-02 05:50:25.929 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:25.935 # compares by a factor of 3.
2025-07-02 05:50:25.942 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:25.948 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:25.961 # of the computation is cached by cruncher
2025-07-02 05:50:25.970 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:25.980 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:25.989 cruncher.ratio() > best_ratio:
2025-07-02 05:50:25.997 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:26.004 if best_ratio < cutoff:
2025-07-02 05:50:26.010 # no non-identical "pretty close" pair
2025-07-02 05:50:26.016 if eqi is None:
2025-07-02 05:50:26.022 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:26.028 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:26.034 return
2025-07-02 05:50:26.047 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:26.059 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:26.070 else:
2025-07-02 05:50:26.083 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:26.094 eqi = None
2025-07-02 05:50:26.103
2025-07-02 05:50:26.113 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:26.125 # identical
2025-07-02 05:50:26.137
2025-07-02 05:50:26.147 # pump out diffs from before the synch point
2025-07-02 05:50:26.160 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:26.169
2025-07-02 05:50:26.179 # do intraline marking on the synch pair
2025-07-02 05:50:26.188 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:26.196 if eqi is None:
2025-07-02 05:50:26.204 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:26.212 atags = btags = ""
2025-07-02 05:50:26.219 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:26.227 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:26.235 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:26.242 if tag == 'replace':
2025-07-02 05:50:26.253 atags += '^' * la
2025-07-02 05:50:26.263 btags += '^' * lb
2025-07-02 05:50:26.270 elif tag == 'delete':
2025-07-02 05:50:26.278 atags += '-' * la
2025-07-02 05:50:26.285 elif tag == 'insert':
2025-07-02 05:50:26.293 btags += '+' * lb
2025-07-02 05:50:26.301 elif tag == 'equal':
2025-07-02 05:50:26.312 atags += ' ' * la
2025-07-02 05:50:26.327 btags += ' ' * lb
2025-07-02 05:50:26.336 else:
2025-07-02 05:50:26.345 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:26.359 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:26.368 else:
2025-07-02 05:50:26.376 # the synch pair is identical
2025-07-02 05:50:26.383 yield '  ' + aelt
2025-07-02 05:50:26.394
2025-07-02 05:50:26.404 # pump out diffs from after the synch point
2025-07-02 05:50:26.412 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:26.419
2025-07-02 05:50:26.425 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:26.430 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:26.435
2025-07-02 05:50:26.441 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:26.451 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:26.465 alo = 316, ahi = 1101
2025-07-02 05:50:26.475 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:26.483 blo = 316, bhi = 1101
2025-07-02 05:50:26.490
2025-07-02 05:50:26.498 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:26.510 g = []
2025-07-02 05:50:26.521 if alo < ahi:
2025-07-02 05:50:26.531 if blo < bhi:
2025-07-02 05:50:26.539 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:26.544 else:
2025-07-02 05:50:26.550 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:26.555 elif blo < bhi:
2025-07-02 05:50:26.560 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:26.566
2025-07-02 05:50:26.572 >       yield from g
2025-07-02 05:50:26.577
2025-07-02 05:50:26.591 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:26.600 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:26.607
2025-07-02 05:50:26.615 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:26.630 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:26.638 alo = 316, ahi = 1101
2025-07-02 05:50:26.645 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:26.651 blo = 316, bhi = 1101
2025-07-02 05:50:26.665
2025-07-02 05:50:26.677 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:26.690 r"""
2025-07-02 05:50:26.703 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:26.712 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:26.722 synch point, and intraline difference marking is done on the
2025-07-02 05:50:26.731 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:26.738
2025-07-02 05:50:26.748 Example:
2025-07-02 05:50:26.758
2025-07-02 05:50:26.765 >>> d = Differ()
2025-07-02 05:50:26.771 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:26.778 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:26.789 >>> print(''.join(results), end="")
2025-07-02 05:50:26.802 - abcDefghiJkl
2025-07-02 05:50:26.819 + abcdefGhijkl
2025-07-02 05:50:26.834 """
2025-07-02 05:50:26.842
2025-07-02 05:50:26.853 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:26.863 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:26.872 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:26.879 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:26.891 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:26.904
2025-07-02 05:50:26.915 # search for the pair that matches best without being identical
2025-07-02 05:50:26.927 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:26.935 # on junk -- unless we have to)
2025-07-02 05:50:26.942 for j in range(blo, bhi):
2025-07-02 05:50:26.951 bj = b[j]
2025-07-02 05:50:26.958 cruncher.set_seq2(bj)
2025-07-02 05:50:26.966 for i in range(alo, ahi):
2025-07-02 05:50:26.976 ai = a[i]
2025-07-02 05:50:26.987 if ai == bj:
2025-07-02 05:50:26.995 if eqi is None:
2025-07-02 05:50:27.003 eqi, eqj = i, j
2025-07-02 05:50:27.015 continue
2025-07-02 05:50:27.027 cruncher.set_seq1(ai)
2025-07-02 05:50:27.036 # computing similarity is expensive, so use the quick
2025-07-02 05:50:27.044 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:27.051 # compares by a factor of 3.
2025-07-02 05:50:27.060 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:27.071 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:27.081 # of the computation is cached by cruncher
2025-07-02 05:50:27.089 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:27.108 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:27.117 cruncher.ratio() > best_ratio:
2025-07-02 05:50:27.124 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:27.130 if best_ratio < cutoff:
2025-07-02 05:50:27.136 # no non-identical "pretty close" pair
2025-07-02 05:50:27.141 if eqi is None:
2025-07-02 05:50:27.146 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:27.151 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:27.157 return
2025-07-02 05:50:27.165 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:27.172 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:27.178 else:
2025-07-02 05:50:27.189 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:27.201 eqi = None
2025-07-02 05:50:27.211
2025-07-02 05:50:27.218 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:27.224 # identical
2025-07-02 05:50:27.230
2025-07-02 05:50:27.238 # pump out diffs from before the synch point
2025-07-02 05:50:27.247 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:27.257
2025-07-02 05:50:27.267 # do intraline marking on the synch pair
2025-07-02 05:50:27.277 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:27.291 if eqi is None:
2025-07-02 05:50:27.303 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:27.314 atags = btags = ""
2025-07-02 05:50:27.324 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:27.331 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:27.339 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:27.346 if tag == 'replace':
2025-07-02 05:50:27.352 atags += '^' * la
2025-07-02 05:50:27.366 btags += '^' * lb
2025-07-02 05:50:27.376 elif tag == 'delete':
2025-07-02 05:50:27.385 atags += '-' * la
2025-07-02 05:50:27.393 elif tag == 'insert':
2025-07-02 05:50:27.399 btags += '+' * lb
2025-07-02 05:50:27.404 elif tag == 'equal':
2025-07-02 05:50:27.411 atags += ' ' * la
2025-07-02 05:50:27.417 btags += ' ' * lb
2025-07-02 05:50:27.422 else:
2025-07-02 05:50:27.433 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:27.444 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:27.452 else:
2025-07-02 05:50:27.458 # the synch pair is identical
2025-07-02 05:50:27.467 yield '  ' + aelt
2025-07-02 05:50:27.475
2025-07-02 05:50:27.482 # pump out diffs from after the synch point
2025-07-02 05:50:27.493 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:27.501
2025-07-02 05:50:27.507 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:27.515 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:27.522
2025-07-02 05:50:27.532 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:27.542 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:27.549 alo = 317, ahi = 1101
2025-07-02 05:50:27.556 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:27.562 blo = 317, bhi = 1101
2025-07-02 05:50:27.566
2025-07-02 05:50:27.572 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:27.577 g = []
2025-07-02 05:50:27.582 if alo < ahi:
2025-07-02 05:50:27.587 if blo < bhi:
2025-07-02 05:50:27.594 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:27.606 else:
2025-07-02 05:50:27.616 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:27.624 elif blo < bhi:
2025-07-02 05:50:27.631 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:27.638
2025-07-02 05:50:27.651 >       yield from g
2025-07-02 05:50:27.661
2025-07-02 05:50:27.668 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:27.674 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:27.685
2025-07-02 05:50:27.696 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:27.706 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:27.717 alo = 317, ahi = 1101
2025-07-02 05:50:27.729 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:27.740 blo = 317, bhi = 1101
2025-07-02 05:50:27.747
2025-07-02 05:50:27.755 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:27.767 r"""
2025-07-02 05:50:27.776 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:27.784 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:27.791 synch point, and intraline difference marking is done on the
2025-07-02 05:50:27.796 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:27.802
2025-07-02 05:50:27.813 Example:
2025-07-02 05:50:27.823
2025-07-02 05:50:27.833 >>> d = Differ()
2025-07-02 05:50:27.842 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:27.855 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:27.864 >>> print(''.join(results), end="")
2025-07-02 05:50:27.872 - abcDefghiJkl
2025-07-02 05:50:27.886 + abcdefGhijkl
2025-07-02 05:50:27.905 """
2025-07-02 05:50:27.913
2025-07-02 05:50:27.921 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:27.928 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:27.934 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:27.943 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:27.955 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:27.964
2025-07-02 05:50:27.977 # search for the pair that matches best without being identical
2025-07-02 05:50:27.986 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:27.994 # on junk -- unless we have to)
2025-07-02 05:50:28.001 for j in range(blo, bhi):
2025-07-02 05:50:28.006 bj = b[j]
2025-07-02 05:50:28.012 cruncher.set_seq2(bj)
2025-07-02 05:50:28.018 for i in range(alo, ahi):
2025-07-02 05:50:28.023 ai = a[i]
2025-07-02 05:50:28.029 if ai == bj:
2025-07-02 05:50:28.035 if eqi is None:
2025-07-02 05:50:28.041 eqi, eqj = i, j
2025-07-02 05:50:28.047 continue
2025-07-02 05:50:28.053 cruncher.set_seq1(ai)
2025-07-02 05:50:28.059 # computing similarity is expensive, so use the quick
2025-07-02 05:50:28.065 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:28.072 # compares by a factor of 3.
2025-07-02 05:50:28.079 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:28.086 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:28.098 # of the computation is cached by cruncher
2025-07-02 05:50:28.109 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:28.119 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:28.127 cruncher.ratio() > best_ratio:
2025-07-02 05:50:28.133 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:28.137 if best_ratio < cutoff:
2025-07-02 05:50:28.142 # no non-identical "pretty close" pair
2025-07-02 05:50:28.147 if eqi is None:
2025-07-02 05:50:28.152 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:28.156 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:28.162 return
2025-07-02 05:50:28.167 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:28.173 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:28.178 else:
2025-07-02 05:50:28.182 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:28.187 eqi = None
2025-07-02 05:50:28.192
2025-07-02 05:50:28.196 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:28.201 # identical
2025-07-02 05:50:28.205
2025-07-02 05:50:28.210 # pump out diffs from before the synch point
2025-07-02 05:50:28.216 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:28.222
2025-07-02 05:50:28.229 # do intraline marking on the synch pair
2025-07-02 05:50:28.235 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:28.241 if eqi is None:
2025-07-02 05:50:28.245 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:28.250 atags = btags = ""
2025-07-02 05:50:28.254 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:28.259 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:28.264 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:28.268 if tag == 'replace':
2025-07-02 05:50:28.273 atags += '^' * la
2025-07-02 05:50:28.278 btags += '^' * lb
2025-07-02 05:50:28.282 elif tag == 'delete':
2025-07-02 05:50:28.287 atags += '-' * la
2025-07-02 05:50:28.292 elif tag == 'insert':
2025-07-02 05:50:28.296 btags += '+' * lb
2025-07-02 05:50:28.301 elif tag == 'equal':
2025-07-02 05:50:28.305 atags += ' ' * la
2025-07-02 05:50:28.310 btags += ' ' * lb
2025-07-02 05:50:28.314 else:
2025-07-02 05:50:28.319 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:28.324 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:28.329 else:
2025-07-02 05:50:28.333 # the synch pair is identical
2025-07-02 05:50:28.338 yield '  ' + aelt
2025-07-02 05:50:28.342
2025-07-02 05:50:28.347 # pump out diffs from after the synch point
2025-07-02 05:50:28.352 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:28.356
2025-07-02 05:50:28.361 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:28.366 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:28.370
2025-07-02 05:50:28.375 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:28.380 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:28.385 alo = 318, ahi = 1101
2025-07-02 05:50:28.390 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:28.395 blo = 318, bhi = 1101
2025-07-02 05:50:28.399
2025-07-02 05:50:28.404 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:28.410 g = []
2025-07-02 05:50:28.415 if alo < ahi:
2025-07-02 05:50:28.420 if blo < bhi:
2025-07-02 05:50:28.424 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:28.429 else:
2025-07-02 05:50:28.434 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:28.438 elif blo < bhi:
2025-07-02 05:50:28.443 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:28.447
2025-07-02 05:50:28.461 >       yield from g
2025-07-02 05:50:28.470
2025-07-02 05:50:28.478 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:28.488 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:28.495
2025-07-02 05:50:28.504 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:28.517 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:28.530 alo = 318, ahi = 1101
2025-07-02 05:50:28.544 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:28.556 blo = 318, bhi = 1101
2025-07-02 05:50:28.565
2025-07-02 05:50:28.572 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:28.579 r"""
2025-07-02 05:50:28.587 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:28.595 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:28.603 synch point, and intraline difference marking is done on the
2025-07-02 05:50:28.611 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:28.617
2025-07-02 05:50:28.631 Example:
2025-07-02 05:50:28.642
2025-07-02 05:50:28.654 >>> d = Differ()
2025-07-02 05:50:28.663 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:28.670 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:28.679 >>> print(''.join(results), end="")
2025-07-02 05:50:28.692 - abcDefghiJkl
2025-07-02 05:50:28.708 + abcdefGhijkl
2025-07-02 05:50:28.722 """
2025-07-02 05:50:28.733
2025-07-02 05:50:28.744 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:28.754 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:28.767 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:28.777 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:28.790 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:28.802
2025-07-02 05:50:28.814 # search for the pair that matches best without being identical
2025-07-02 05:50:28.825 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:28.832 # on junk -- unless we have to)
2025-07-02 05:50:28.839 for j in range(blo, bhi):
2025-07-02 05:50:28.846 bj = b[j]
2025-07-02 05:50:28.852 cruncher.set_seq2(bj)
2025-07-02 05:50:28.857 for i in range(alo, ahi):
2025-07-02 05:50:28.862 ai = a[i]
2025-07-02 05:50:28.867 if ai == bj:
2025-07-02 05:50:28.875 if eqi is None:
2025-07-02 05:50:28.885 eqi, eqj = i, j
2025-07-02 05:50:28.897 continue
2025-07-02 05:50:28.909 cruncher.set_seq1(ai)
2025-07-02 05:50:28.921 # computing similarity is expensive, so use the quick
2025-07-02 05:50:28.930 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:28.937 # compares by a factor of 3.
2025-07-02 05:50:28.947 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:28.955 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:28.962 # of the computation is cached by cruncher
2025-07-02 05:50:28.971 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:28.983 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:28.991 cruncher.ratio() > best_ratio:
2025-07-02 05:50:28.998 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:29.004 if best_ratio < cutoff:
2025-07-02 05:50:29.010 # no non-identical "pretty close" pair
2025-07-02 05:50:29.017 if eqi is None:
2025-07-02 05:50:29.024 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:29.032 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:29.038 return
2025-07-02 05:50:29.050 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:29.061 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:29.073 else:
2025-07-02 05:50:29.086 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:29.096 eqi = None
2025-07-02 05:50:29.105
2025-07-02 05:50:29.112 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:29.119 # identical
2025-07-02 05:50:29.126
2025-07-02 05:50:29.132 # pump out diffs from before the synch point
2025-07-02 05:50:29.139 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:29.146
2025-07-02 05:50:29.159 # do intraline marking on the synch pair
2025-07-02 05:50:29.168 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:29.176 if eqi is None:
2025-07-02 05:50:29.183 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:29.190 atags = btags = ""
2025-07-02 05:50:29.201 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:29.210 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:29.219 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:29.227 if tag == 'replace':
2025-07-02 05:50:29.234 atags += '^' * la
2025-07-02 05:50:29.245 btags += '^' * lb
2025-07-02 05:50:29.253 elif tag == 'delete':
2025-07-02 05:50:29.261 atags += '-' * la
2025-07-02 05:50:29.268 elif tag == 'insert':
2025-07-02 05:50:29.274 btags += '+' * lb
2025-07-02 05:50:29.280 elif tag == 'equal':
2025-07-02 05:50:29.288 atags += ' ' * la
2025-07-02 05:50:29.297 btags += ' ' * lb
2025-07-02 05:50:29.310 else:
2025-07-02 05:50:29.321 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:29.334 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:29.346 else:
2025-07-02 05:50:29.356 # the synch pair is identical
2025-07-02 05:50:29.365 yield '  ' + aelt
2025-07-02 05:50:29.377
2025-07-02 05:50:29.389 # pump out diffs from after the synch point
2025-07-02 05:50:29.395 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:29.401
2025-07-02 05:50:29.407 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:29.414 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:29.421
2025-07-02 05:50:29.427 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:29.442 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:29.452 alo = 319, ahi = 1101
2025-07-02 05:50:29.460 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:29.468 blo = 319, bhi = 1101
2025-07-02 05:50:29.474
2025-07-02 05:50:29.487 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:29.495 g = []
2025-07-02 05:50:29.501 if alo < ahi:
2025-07-02 05:50:29.514 if blo < bhi:
2025-07-02 05:50:29.524 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:29.534 else:
2025-07-02 05:50:29.546 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:29.556 elif blo < bhi:
2025-07-02 05:50:29.569 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:29.581
2025-07-02 05:50:29.591 >       yield from g
2025-07-02 05:50:29.600
2025-07-02 05:50:29.611 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:29.624 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:29.636
2025-07-02 05:50:29.645 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:29.653 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:29.659 alo = 319, ahi = 1101
2025-07-02 05:50:29.667 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:29.675 blo = 319, bhi = 1101
2025-07-02 05:50:29.685
2025-07-02 05:50:29.694 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:29.701 r"""
2025-07-02 05:50:29.711 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:29.718 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:29.725 synch point, and intraline difference marking is done on the
2025-07-02 05:50:29.732 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:29.737
2025-07-02 05:50:29.743 Example:
2025-07-02 05:50:29.754
2025-07-02 05:50:29.764 >>> d = Differ()
2025-07-02 05:50:29.772 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:29.779 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:29.785 >>> print(''.join(results), end="")
2025-07-02 05:50:29.791 - abcDefghiJkl
2025-07-02 05:50:29.803 + abcdefGhijkl
2025-07-02 05:50:29.815 """
2025-07-02 05:50:29.825
2025-07-02 05:50:29.837 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:29.846 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:29.853 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:29.859 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:29.866 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:29.876
2025-07-02 05:50:29.888 # search for the pair that matches best without being identical
2025-07-02 05:50:29.902 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:29.914 # on junk -- unless we have to)
2025-07-02 05:50:29.926 for j in range(blo, bhi):
2025-07-02 05:50:29.939 bj = b[j]
2025-07-02 05:50:29.952 cruncher.set_seq2(bj)
2025-07-02 05:50:29.964 for i in range(alo, ahi):
2025-07-02 05:50:29.976 ai = a[i]
2025-07-02 05:50:29.988 if ai == bj:
2025-07-02 05:50:29.999 if eqi is None:
2025-07-02 05:50:30.008 eqi, eqj = i, j
2025-07-02 05:50:30.018 continue
2025-07-02 05:50:30.030 cruncher.set_seq1(ai)
2025-07-02 05:50:30.040 # computing similarity is expensive, so use the quick
2025-07-02 05:50:30.047 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:30.054 # compares by a factor of 3.
2025-07-02 05:50:30.060 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:30.065 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:30.071 # of the computation is cached by cruncher
2025-07-02 05:50:30.076 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:30.083 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:30.090 cruncher.ratio() > best_ratio:
2025-07-02 05:50:30.099 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:30.110 if best_ratio < cutoff:
2025-07-02 05:50:30.119 # no non-identical "pretty close" pair
2025-07-02 05:50:30.130 if eqi is None:
2025-07-02 05:50:30.142 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:30.153 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:30.163 return
2025-07-02 05:50:30.173 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:30.181 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:30.191 else:
2025-07-02 05:50:30.202 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:30.211 eqi = None
2025-07-02 05:50:30.218
2025-07-02 05:50:30.226 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:30.231 # identical
2025-07-02 05:50:30.239
2025-07-02 05:50:30.253 # pump out diffs from before the synch point
2025-07-02 05:50:30.261 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:30.267
2025-07-02 05:50:30.274 # do intraline marking on the synch pair
2025-07-02 05:50:30.281 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:30.287 if eqi is None:
2025-07-02 05:50:30.295 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:30.307 atags = btags = ""
2025-07-02 05:50:30.316 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:30.322 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:30.329 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:30.336 if tag == 'replace':
2025-07-02 05:50:30.343 atags += '^' * la
2025-07-02 05:50:30.356 btags += '^' * lb
2025-07-02 05:50:30.369 elif tag == 'delete':
2025-07-02 05:50:30.380 atags += '-' * la
2025-07-02 05:50:30.388 elif tag == 'insert':
2025-07-02 05:50:30.395 btags += '+' * lb
2025-07-02 05:50:30.403 elif tag == 'equal':
2025-07-02 05:50:30.414 atags += ' ' * la
2025-07-02 05:50:30.424 btags += ' ' * lb
2025-07-02 05:50:30.432 else:
2025-07-02 05:50:30.439 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:30.449 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:30.461 else:
2025-07-02 05:50:30.472 # the synch pair is identical
2025-07-02 05:50:30.482 yield '  ' + aelt
2025-07-02 05:50:30.493
2025-07-02 05:50:30.503 # pump out diffs from after the synch point
2025-07-02 05:50:30.513 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:30.523
2025-07-02 05:50:30.536 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:30.548 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:30.561
2025-07-02 05:50:30.572 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:30.586 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:30.599 alo = 320, ahi = 1101
2025-07-02 05:50:30.612 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:30.623 blo = 320, bhi = 1101
2025-07-02 05:50:30.632
2025-07-02 05:50:30.641 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:30.648 g = []
2025-07-02 05:50:30.655 if alo < ahi:
2025-07-02 05:50:30.663 if blo < bhi:
2025-07-02 05:50:30.673 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:30.681 else:
2025-07-02 05:50:30.690 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:30.697 elif blo < bhi:
2025-07-02 05:50:30.703 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:30.709
2025-07-02 05:50:30.716 >       yield from g
2025-07-02 05:50:30.721
2025-07-02 05:50:30.728 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:30.734 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:30.745
2025-07-02 05:50:30.753 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:30.762 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:30.775 alo = 320, ahi = 1101
2025-07-02 05:50:30.788 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:30.796 blo = 320, bhi = 1101
2025-07-02 05:50:30.803
2025-07-02 05:50:30.811 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:30.817 r"""
2025-07-02 05:50:30.829 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:30.839 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:30.847 synch point, and intraline difference marking is done on the
2025-07-02 05:50:30.857 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:30.867
2025-07-02 05:50:30.877 Example:
2025-07-02 05:50:30.884
2025-07-02 05:50:30.892 >>> d = Differ()
2025-07-02 05:50:30.899 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:30.907 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:30.915 >>> print(''.join(results), end="")
2025-07-02 05:50:30.922 - abcDefghiJkl
2025-07-02 05:50:30.935 + abcdefGhijkl
2025-07-02 05:50:30.951 """
2025-07-02 05:50:30.964
2025-07-02 05:50:30.975 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:30.984 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:30.992 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:30.999 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:31.006 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:31.020
2025-07-02 05:50:31.035 # search for the pair that matches best without being identical
2025-07-02 05:50:31.044 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:31.052 # on junk -- unless we have to)
2025-07-02 05:50:31.058 for j in range(blo, bhi):
2025-07-02 05:50:31.065 bj = b[j]
2025-07-02 05:50:31.072 cruncher.set_seq2(bj)
2025-07-02 05:50:31.078 for i in range(alo, ahi):
2025-07-02 05:50:31.085 ai = a[i]
2025-07-02 05:50:31.090 if ai == bj:
2025-07-02 05:50:31.095 if eqi is None:
2025-07-02 05:50:31.100 eqi, eqj = i, j
2025-07-02 05:50:31.105 continue
2025-07-02 05:50:31.110 cruncher.set_seq1(ai)
2025-07-02 05:50:31.115 # computing similarity is expensive, so use the quick
2025-07-02 05:50:31.124 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:31.132 # compares by a factor of 3.
2025-07-02 05:50:31.140 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:31.148 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:31.157 # of the computation is cached by cruncher
2025-07-02 05:50:31.170 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:31.179 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:31.193 cruncher.ratio() > best_ratio:
2025-07-02 05:50:31.203 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:31.212 if best_ratio < cutoff:
2025-07-02 05:50:31.227 # no non-identical "pretty close" pair
2025-07-02 05:50:31.241 if eqi is None:
2025-07-02 05:50:31.255 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:31.265 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:31.271 return
2025-07-02 05:50:31.278 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:31.288 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:31.300 else:
2025-07-02 05:50:31.310 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:31.323 eqi = None
2025-07-02 05:50:31.336
2025-07-02 05:50:31.347 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:31.361 # identical
2025-07-02 05:50:31.371
2025-07-02 05:50:31.384 # pump out diffs from before the synch point
2025-07-02 05:50:31.395 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:31.407
2025-07-02 05:50:31.417 # do intraline marking on the synch pair
2025-07-02 05:50:31.424 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:31.430 if eqi is None:
2025-07-02 05:50:31.435 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:31.441 atags = btags = ""
2025-07-02 05:50:31.446 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:31.452 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:31.458 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:31.464 if tag == 'replace':
2025-07-02 05:50:31.470 atags += '^' * la
2025-07-02 05:50:31.477 btags += '^' * lb
2025-07-02 05:50:31.484 elif tag == 'delete':
2025-07-02 05:50:31.490 atags += '-' * la
2025-07-02 05:50:31.495 elif tag == 'insert':
2025-07-02 05:50:31.499 btags += '+' * lb
2025-07-02 05:50:31.504 elif tag == 'equal':
2025-07-02 05:50:31.509 atags += ' ' * la
2025-07-02 05:50:31.517 btags += ' ' * lb
2025-07-02 05:50:31.525 else:
2025-07-02 05:50:31.531 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:31.539 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:31.547 else:
2025-07-02 05:50:31.555 # the synch pair is identical
2025-07-02 05:50:31.563 yield '  ' + aelt
2025-07-02 05:50:31.568
2025-07-02 05:50:31.582 # pump out diffs from after the synch point
2025-07-02 05:50:31.591 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:31.598
2025-07-02 05:50:31.609 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:31.619 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:31.628
2025-07-02 05:50:31.636 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:31.645 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:31.652 alo = 321, ahi = 1101
2025-07-02 05:50:31.665 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:31.674 blo = 321, bhi = 1101
2025-07-02 05:50:31.682
2025-07-02 05:50:31.692 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:31.706 g = []
2025-07-02 05:50:31.717 if alo < ahi:
2025-07-02 05:50:31.729 if blo < bhi:
2025-07-02 05:50:31.740 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:31.748 else:
2025-07-02 05:50:31.757 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:31.769 elif blo < bhi:
2025-07-02 05:50:31.781 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:31.794
2025-07-02 05:50:31.804 >       yield from g
2025-07-02 05:50:31.812
2025-07-02 05:50:31.818 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:31.824 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:31.830
2025-07-02 05:50:31.836 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:31.845 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:31.851 alo = 321, ahi = 1101
2025-07-02 05:50:31.859 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:31.867 blo = 321, bhi = 1101
2025-07-02 05:50:31.879
2025-07-02 05:50:31.885 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:31.891 r"""
2025-07-02 05:50:31.897 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:31.905 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:31.912 synch point, and intraline difference marking is done on the
2025-07-02 05:50:31.918 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:31.928
2025-07-02 05:50:31.938 Example:
2025-07-02 05:50:31.946
2025-07-02 05:50:31.955 >>> d = Differ()
2025-07-02 05:50:31.968 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:31.977 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:31.984 >>> print(''.join(results), end="")
2025-07-02 05:50:31.991 - abcDefghiJkl
2025-07-02 05:50:32.002 + abcdefGhijkl
2025-07-02 05:50:32.013 """
2025-07-02 05:50:32.018
2025-07-02 05:50:32.023 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:32.029 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:32.034 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:32.040 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:32.045 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:32.052
2025-07-02 05:50:32.058 # search for the pair that matches best without being identical
2025-07-02 05:50:32.065 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:32.072 # on junk -- unless we have to)
2025-07-02 05:50:32.085 for j in range(blo, bhi):
2025-07-02 05:50:32.094 bj = b[j]
2025-07-02 05:50:32.102 cruncher.set_seq2(bj)
2025-07-02 05:50:32.110 for i in range(alo, ahi):
2025-07-02 05:50:32.117 ai = a[i]
2025-07-02 05:50:32.128 if ai == bj:
2025-07-02 05:50:32.139 if eqi is None:
2025-07-02 05:50:32.147 eqi, eqj = i, j
2025-07-02 05:50:32.155 continue
2025-07-02 05:50:32.165 cruncher.set_seq1(ai)
2025-07-02 05:50:32.179 # computing similarity is expensive, so use the quick
2025-07-02 05:50:32.192 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:32.205 # compares by a factor of 3.
2025-07-02 05:50:32.215 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:32.229 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:32.238 # of the computation is cached by cruncher
2025-07-02 05:50:32.245 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:32.251 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:32.258 cruncher.ratio() > best_ratio:
2025-07-02 05:50:32.270 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:32.279 if best_ratio < cutoff:
2025-07-02 05:50:32.287 # no non-identical "pretty close" pair
2025-07-02 05:50:32.295 if eqi is None:
2025-07-02 05:50:32.302 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:32.310 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:32.319 return
2025-07-02 05:50:32.332 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:32.340 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:32.348 else:
2025-07-02 05:50:32.355 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:32.368 eqi = None
2025-07-02 05:50:32.379
2025-07-02 05:50:32.386 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:32.392 # identical
2025-07-02 05:50:32.398
2025-07-02 05:50:32.403 # pump out diffs from before the synch point
2025-07-02 05:50:32.409 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:32.422
2025-07-02 05:50:32.430 # do intraline marking on the synch pair
2025-07-02 05:50:32.437 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:32.445 if eqi is None:
2025-07-02 05:50:32.456 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:32.465 atags = btags = ""
2025-07-02 05:50:32.471 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:32.477 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:32.483 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:32.489 if tag == 'replace':
2025-07-02 05:50:32.495 atags += '^' * la
2025-07-02 05:50:32.503 btags += '^' * lb
2025-07-02 05:50:32.514 elif tag == 'delete':
2025-07-02 05:50:32.524 atags += '-' * la
2025-07-02 05:50:32.536 elif tag == 'insert':
2025-07-02 05:50:32.547 btags += '+' * lb
2025-07-02 05:50:32.555 elif tag == 'equal':
2025-07-02 05:50:32.562 atags += ' ' * la
2025-07-02 05:50:32.573 btags += ' ' * lb
2025-07-02 05:50:32.578 else:
2025-07-02 05:50:32.586 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:32.599 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:32.611 else:
2025-07-02 05:50:32.621 # the synch pair is identical
2025-07-02 05:50:32.635 yield '  ' + aelt
2025-07-02 05:50:32.643
2025-07-02 05:50:32.651 # pump out diffs from after the synch point
2025-07-02 05:50:32.659 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:32.668
2025-07-02 05:50:32.681 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:32.691 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:32.700
2025-07-02 05:50:32.707 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:32.715 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:32.728 alo = 322, ahi = 1101
2025-07-02 05:50:32.737 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:32.749 blo = 322, bhi = 1101
2025-07-02 05:50:32.759
2025-07-02 05:50:32.766 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:32.773 g = []
2025-07-02 05:50:32.780 if alo < ahi:
2025-07-02 05:50:32.793 if blo < bhi:
2025-07-02 05:50:32.806 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:32.817 else:
2025-07-02 05:50:32.825 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:32.838 elif blo < bhi:
2025-07-02 05:50:32.848 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:32.857
2025-07-02 05:50:32.870 >       yield from g
2025-07-02 05:50:32.882
2025-07-02 05:50:32.893 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:32.904 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:32.918
2025-07-02 05:50:32.931 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:32.942 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:32.950 alo = 322, ahi = 1101
2025-07-02 05:50:32.958 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:32.965 blo = 322, bhi = 1101
2025-07-02 05:50:32.971
2025-07-02 05:50:32.979 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:32.992 r"""
2025-07-02 05:50:33.000 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:33.007 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:33.015 synch point, and intraline difference marking is done on the
2025-07-02 05:50:33.021 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:33.026
2025-07-02 05:50:33.031 Example:
2025-07-02 05:50:33.036
2025-07-02 05:50:33.042 >>> d = Differ()
2025-07-02 05:50:33.047 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:33.053 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:33.059 >>> print(''.join(results), end="")
2025-07-02 05:50:33.066 - abcDefghiJkl
2025-07-02 05:50:33.086 + abcdefGhijkl
2025-07-02 05:50:33.106 """
2025-07-02 05:50:33.117
2025-07-02 05:50:33.128 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:33.140 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:33.149 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:33.157 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:33.164 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:33.168
2025-07-02 05:50:33.173 # search for the pair that matches best without being identical
2025-07-02 05:50:33.178 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:33.182 # on junk -- unless we have to)
2025-07-02 05:50:33.191 for j in range(blo, bhi):
2025-07-02 05:50:33.203 bj = b[j]
2025-07-02 05:50:33.210 cruncher.set_seq2(bj)
2025-07-02 05:50:33.220 for i in range(alo, ahi):
2025-07-02 05:50:33.231 ai = a[i]
2025-07-02 05:50:33.240 if ai == bj:
2025-07-02 05:50:33.254 if eqi is None:
2025-07-02 05:50:33.266 eqi, eqj = i, j
2025-07-02 05:50:33.275 continue
2025-07-02 05:50:33.282 cruncher.set_seq1(ai)
2025-07-02 05:50:33.294 # computing similarity is expensive, so use the quick
2025-07-02 05:50:33.306 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:33.319 # compares by a factor of 3.
2025-07-02 05:50:33.329 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:33.336 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:33.342 # of the computation is cached by cruncher
2025-07-02 05:50:33.349 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:33.356 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:33.362 cruncher.ratio() > best_ratio:
2025-07-02 05:50:33.369 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:33.375 if best_ratio < cutoff:
2025-07-02 05:50:33.382 # no non-identical "pretty close" pair
2025-07-02 05:50:33.388 if eqi is None:
2025-07-02 05:50:33.395 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:33.403 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:33.411 return
2025-07-02 05:50:33.421 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:33.428 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:33.434 else:
2025-07-02 05:50:33.440 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:33.446 eqi = None
2025-07-02 05:50:33.452
2025-07-02 05:50:33.457 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:33.463 # identical
2025-07-02 05:50:33.469
2025-07-02 05:50:33.474 # pump out diffs from before the synch point
2025-07-02 05:50:33.481 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:33.488
2025-07-02 05:50:33.495 # do intraline marking on the synch pair
2025-07-02 05:50:33.500 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:33.507 if eqi is None:
2025-07-02 05:50:33.519 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:33.526 atags = btags = ""
2025-07-02 05:50:33.533 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:33.540 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:33.547 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:33.553 if tag == 'replace':
2025-07-02 05:50:33.564 atags += '^' * la
2025-07-02 05:50:33.574 btags += '^' * lb
2025-07-02 05:50:33.583 elif tag == 'delete':
2025-07-02 05:50:33.593 atags += '-' * la
2025-07-02 05:50:33.607 elif tag == 'insert':
2025-07-02 05:50:33.620 btags += '+' * lb
2025-07-02 05:50:33.631 elif tag == 'equal':
2025-07-02 05:50:33.640 atags += ' ' * la
2025-07-02 05:50:33.653 btags += ' ' * lb
2025-07-02 05:50:33.666 else:
2025-07-02 05:50:33.676 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:33.685 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:33.697 else:
2025-07-02 05:50:33.711 # the synch pair is identical
2025-07-02 05:50:33.722 yield '  ' + aelt
2025-07-02 05:50:33.735
2025-07-02 05:50:33.744 # pump out diffs from after the synch point
2025-07-02 05:50:33.753 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:33.760
2025-07-02 05:50:33.767 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:33.772 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:33.777
2025-07-02 05:50:33.784 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:33.790 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:33.803 alo = 323, ahi = 1101
2025-07-02 05:50:33.812 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:33.818 blo = 323, bhi = 1101
2025-07-02 05:50:33.823
2025-07-02 05:50:33.832 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:33.842 g = []
2025-07-02 05:50:33.850 if alo < ahi:
2025-07-02 05:50:33.858 if blo < bhi:
2025-07-02 05:50:33.865 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:33.870 else:
2025-07-02 05:50:33.876 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:33.883 elif blo < bhi:
2025-07-02 05:50:33.893 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:33.906
2025-07-02 05:50:33.916 >       yield from g
2025-07-02 05:50:33.925
2025-07-02 05:50:33.932 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:33.939 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:33.945
2025-07-02 05:50:33.952 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:33.959 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:33.967 alo = 323, ahi = 1101
2025-07-02 05:50:33.977 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:33.989 blo = 323, bhi = 1101
2025-07-02 05:50:33.999
2025-07-02 05:50:34.013 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:34.024 r"""
2025-07-02 05:50:34.036 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:34.046 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:34.055 synch point, and intraline difference marking is done on the
2025-07-02 05:50:34.069 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:34.081
2025-07-02 05:50:34.092 Example:
2025-07-02 05:50:34.105
2025-07-02 05:50:34.120 >>> d = Differ()
2025-07-02 05:50:34.134 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:34.147 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:34.158 >>> print(''.join(results), end="")
2025-07-02 05:50:34.166 - abcDefghiJkl
2025-07-02 05:50:34.179 + abcdefGhijkl
2025-07-02 05:50:34.199 """
2025-07-02 05:50:34.207
2025-07-02 05:50:34.216 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:34.224 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:34.230 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:34.245 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:34.258 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:34.269
2025-07-02 05:50:34.280 # search for the pair that matches best without being identical
2025-07-02 05:50:34.291 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:34.304 # on junk -- unless we have to)
2025-07-02 05:50:34.315 for j in range(blo, bhi):
2025-07-02 05:50:34.323 bj = b[j]
2025-07-02 05:50:34.331 cruncher.set_seq2(bj)
2025-07-02 05:50:34.339 for i in range(alo, ahi):
2025-07-02 05:50:34.353 ai = a[i]
2025-07-02 05:50:34.364 if ai == bj:
2025-07-02 05:50:34.378 if eqi is None:
2025-07-02 05:50:34.388 eqi, eqj = i, j
2025-07-02 05:50:34.395 continue
2025-07-02 05:50:34.407 cruncher.set_seq1(ai)
2025-07-02 05:50:34.419 # computing similarity is expensive, so use the quick
2025-07-02 05:50:34.428 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:34.436 # compares by a factor of 3.
2025-07-02 05:50:34.445 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:34.454 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:34.461 # of the computation is cached by cruncher
2025-07-02 05:50:34.470 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:34.479 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:34.492 cruncher.ratio() > best_ratio:
2025-07-02 05:50:34.501 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:34.509 if best_ratio < cutoff:
2025-07-02 05:50:34.516 # no non-identical "pretty close" pair
2025-07-02 05:50:34.522 if eqi is None:
2025-07-02 05:50:34.532 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:34.544 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:34.555 return
2025-07-02 05:50:34.567 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:34.577 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:34.585 else:
2025-07-02 05:50:34.592 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:34.601 eqi = None
2025-07-02 05:50:34.607
2025-07-02 05:50:34.613 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:34.619 # identical
2025-07-02 05:50:34.624
2025-07-02 05:50:34.629 # pump out diffs from before the synch point
2025-07-02 05:50:34.634 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:34.639
2025-07-02 05:50:34.644 # do intraline marking on the synch pair
2025-07-02 05:50:34.649 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:34.655 if eqi is None:
2025-07-02 05:50:34.662 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:34.669 atags = btags = ""
2025-07-02 05:50:34.680 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:34.689 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:34.697 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:34.709 if tag == 'replace':
2025-07-02 05:50:34.718 atags += '^' * la
2025-07-02 05:50:34.727 btags += '^' * lb
2025-07-02 05:50:34.739 elif tag == 'delete':
2025-07-02 05:50:34.751 atags += '-' * la
2025-07-02 05:50:34.761 elif tag == 'insert':
2025-07-02 05:50:34.769 btags += '+' * lb
2025-07-02 05:50:34.776 elif tag == 'equal':
2025-07-02 05:50:34.783 atags += ' ' * la
2025-07-02 05:50:34.790 btags += ' ' * lb
2025-07-02 05:50:34.798 else:
2025-07-02 05:50:34.807 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:34.819 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:34.830 else:
2025-07-02 05:50:34.843 # the synch pair is identical
2025-07-02 05:50:34.851 yield '  ' + aelt
2025-07-02 05:50:34.857
2025-07-02 05:50:34.863 # pump out diffs from after the synch point
2025-07-02 05:50:34.868 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:34.873
2025-07-02 05:50:34.878 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:34.883 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:34.888
2025-07-02 05:50:34.893 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:34.898 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:34.904 alo = 324, ahi = 1101
2025-07-02 05:50:34.909 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:34.914 blo = 324, bhi = 1101
2025-07-02 05:50:34.920
2025-07-02 05:50:34.926 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:34.932 g = []
2025-07-02 05:50:34.939 if alo < ahi:
2025-07-02 05:50:34.947 if blo < bhi:
2025-07-02 05:50:34.956 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:34.963 else:
2025-07-02 05:50:34.970 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:34.976 elif blo < bhi:
2025-07-02 05:50:34.982 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:34.988
2025-07-02 05:50:34.994 >       yield from g
2025-07-02 05:50:35.000
2025-07-02 05:50:35.006 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:35.012 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:35.019
2025-07-02 05:50:35.025 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:35.033 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:35.039 alo = 324, ahi = 1101
2025-07-02 05:50:35.046 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:35.052 blo = 324, bhi = 1101
2025-07-02 05:50:35.058
2025-07-02 05:50:35.065 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:35.070 r"""
2025-07-02 05:50:35.076 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:35.083 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:35.088 synch point, and intraline difference marking is done on the
2025-07-02 05:50:35.095 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:35.103
2025-07-02 05:50:35.111 Example:
2025-07-02 05:50:35.119
2025-07-02 05:50:35.130 >>> d = Differ()
2025-07-02 05:50:35.139 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:35.147 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:35.155 >>> print(''.join(results), end="")
2025-07-02 05:50:35.167 - abcDefghiJkl
2025-07-02 05:50:35.189 + abcdefGhijkl
2025-07-02 05:50:35.205 """
2025-07-02 05:50:35.211
2025-07-02 05:50:35.225 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:35.236 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:35.246 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:35.257 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:35.269 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:35.278
2025-07-02 05:50:35.286 # search for the pair that matches best without being identical
2025-07-02 05:50:35.296 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:35.309 # on junk -- unless we have to)
2025-07-02 05:50:35.318 for j in range(blo, bhi):
2025-07-02 05:50:35.330 bj = b[j]
2025-07-02 05:50:35.341 cruncher.set_seq2(bj)
2025-07-02 05:50:35.351 for i in range(alo, ahi):
2025-07-02 05:50:35.359 ai = a[i]
2025-07-02 05:50:35.369 if ai == bj:
2025-07-02 05:50:35.380 if eqi is None:
2025-07-02 05:50:35.389 eqi, eqj = i, j
2025-07-02 05:50:35.396 continue
2025-07-02 05:50:35.409 cruncher.set_seq1(ai)
2025-07-02 05:50:35.421 # computing similarity is expensive, so use the quick
2025-07-02 05:50:35.431 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:35.440 # compares by a factor of 3.
2025-07-02 05:50:35.451 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:35.460 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:35.469 # of the computation is cached by cruncher
2025-07-02 05:50:35.476 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:35.483 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:35.490 cruncher.ratio() > best_ratio:
2025-07-02 05:50:35.505 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:35.518 if best_ratio < cutoff:
2025-07-02 05:50:35.529 # no non-identical "pretty close" pair
2025-07-02 05:50:35.536 if eqi is None:
2025-07-02 05:50:35.544 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:35.551 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:35.557 return
2025-07-02 05:50:35.570 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:35.581 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:35.591 else:
2025-07-02 05:50:35.600 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:35.608 eqi = None
2025-07-02 05:50:35.615
2025-07-02 05:50:35.622 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:35.629 # identical
2025-07-02 05:50:35.635
2025-07-02 05:50:35.642 # pump out diffs from before the synch point
2025-07-02 05:50:35.648 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:35.654
2025-07-02 05:50:35.660 # do intraline marking on the synch pair
2025-07-02 05:50:35.666 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:35.672 if eqi is None:
2025-07-02 05:50:35.678 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:35.692 atags = btags = ""
2025-07-02 05:50:35.703 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:35.711 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:35.720 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:35.734 if tag == 'replace':
2025-07-02 05:50:35.748 atags += '^' * la
2025-07-02 05:50:35.760 btags += '^' * lb
2025-07-02 05:50:35.768 elif tag == 'delete':
2025-07-02 05:50:35.776 atags += '-' * la
2025-07-02 05:50:35.783 elif tag == 'insert':
2025-07-02 05:50:35.793 btags += '+' * lb
2025-07-02 05:50:35.804 elif tag == 'equal':
2025-07-02 05:50:35.813 atags += ' ' * la
2025-07-02 05:50:35.822 btags += ' ' * lb
2025-07-02 05:50:35.836 else:
2025-07-02 05:50:35.851 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:35.861 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:35.870 else:
2025-07-02 05:50:35.878 # the synch pair is identical
2025-07-02 05:50:35.884 yield '  ' + aelt
2025-07-02 05:50:35.890
2025-07-02 05:50:35.895 # pump out diffs from after the synch point
2025-07-02 05:50:35.900 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:35.905
2025-07-02 05:50:35.913 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:35.920 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:35.926
2025-07-02 05:50:35.931 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:35.939 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:35.945 alo = 325, ahi = 1101
2025-07-02 05:50:35.952 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:35.959 blo = 325, bhi = 1101
2025-07-02 05:50:35.967
2025-07-02 05:50:35.979 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:35.987 g = []
2025-07-02 05:50:36.000 if alo < ahi:
2025-07-02 05:50:36.012 if blo < bhi:
2025-07-02 05:50:36.025 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:36.036 else:
2025-07-02 05:50:36.044 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:36.052 elif blo < bhi:
2025-07-02 05:50:36.064 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:36.076
2025-07-02 05:50:36.086 >       yield from g
2025-07-02 05:50:36.093
2025-07-02 05:50:36.101 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:36.108 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:36.114
2025-07-02 05:50:36.120 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:36.128 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:36.134 alo = 325, ahi = 1101
2025-07-02 05:50:36.143 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:36.150 blo = 325, bhi = 1101
2025-07-02 05:50:36.157
2025-07-02 05:50:36.166 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:36.175 r"""
2025-07-02 05:50:36.185 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:36.193 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:36.201 synch point, and intraline difference marking is done on the
2025-07-02 05:50:36.208 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:36.214
2025-07-02 05:50:36.220 Example:
2025-07-02 05:50:36.226
2025-07-02 05:50:36.232 >>> d = Differ()
2025-07-02 05:50:36.238 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:36.244 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:36.250 >>> print(''.join(results), end="")
2025-07-02 05:50:36.256 - abcDefghiJkl
2025-07-02 05:50:36.268 + abcdefGhijkl
2025-07-02 05:50:36.286 """
2025-07-02 05:50:36.295
2025-07-02 05:50:36.307 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:36.319 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:36.330 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:36.339 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:36.347 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:36.356
2025-07-02 05:50:36.366 # search for the pair that matches best without being identical
2025-07-02 05:50:36.376 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:36.384 # on junk -- unless we have to)
2025-07-02 05:50:36.389 for j in range(blo, bhi):
2025-07-02 05:50:36.394 bj = b[j]
2025-07-02 05:50:36.399 cruncher.set_seq2(bj)
2025-07-02 05:50:36.403 for i in range(alo, ahi):
2025-07-02 05:50:36.408 ai = a[i]
2025-07-02 05:50:36.412 if ai == bj:
2025-07-02 05:50:36.417 if eqi is None:
2025-07-02 05:50:36.421 eqi, eqj = i, j
2025-07-02 05:50:36.425 continue
2025-07-02 05:50:36.430 cruncher.set_seq1(ai)
2025-07-02 05:50:36.435 # computing similarity is expensive, so use the quick
2025-07-02 05:50:36.440 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:36.445 # compares by a factor of 3.
2025-07-02 05:50:36.450 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:36.454 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:36.459 # of the computation is cached by cruncher
2025-07-02 05:50:36.465 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:36.471 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:36.476 cruncher.ratio() > best_ratio:
2025-07-02 05:50:36.480 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:36.485 if best_ratio < cutoff:
2025-07-02 05:50:36.490 # no non-identical "pretty close" pair
2025-07-02 05:50:36.500 if eqi is None:
2025-07-02 05:50:36.512 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:36.523 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:36.536 return
2025-07-02 05:50:36.545 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:36.555 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:36.566 else:
2025-07-02 05:50:36.579 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:36.589 eqi = None
2025-07-02 05:50:36.597
2025-07-02 05:50:36.603 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:36.608 # identical
2025-07-02 05:50:36.612
2025-07-02 05:50:36.625 # pump out diffs from before the synch point
2025-07-02 05:50:36.637 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:36.649
2025-07-02 05:50:36.658 # do intraline marking on the synch pair
2025-07-02 05:50:36.667 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:36.676 if eqi is None:
2025-07-02 05:50:36.685 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:36.699 atags = btags = ""
2025-07-02 05:50:36.711 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:36.721 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:36.727 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:36.734 if tag == 'replace':
2025-07-02 05:50:36.746 atags += '^' * la
2025-07-02 05:50:36.755 btags += '^' * lb
2025-07-02 05:50:36.762 elif tag == 'delete':
2025-07-02 05:50:36.771 atags += '-' * la
2025-07-02 05:50:36.781 elif tag == 'insert':
2025-07-02 05:50:36.790 btags += '+' * lb
2025-07-02 05:50:36.801 elif tag == 'equal':
2025-07-02 05:50:36.814 atags += ' ' * la
2025-07-02 05:50:36.828 btags += ' ' * lb
2025-07-02 05:50:36.839 else:
2025-07-02 05:50:36.850 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:36.861 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:36.874 else:
2025-07-02 05:50:36.885 # the synch pair is identical
2025-07-02 05:50:36.897 yield '  ' + aelt
2025-07-02 05:50:36.910
2025-07-02 05:50:36.921 # pump out diffs from after the synch point
2025-07-02 05:50:36.929 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:36.935
2025-07-02 05:50:36.943 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:36.955 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:36.964
2025-07-02 05:50:36.971 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:36.984 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:36.995 alo = 326, ahi = 1101
2025-07-02 05:50:37.005 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:37.014 blo = 326, bhi = 1101
2025-07-02 05:50:37.023
2025-07-02 05:50:37.036 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:37.047 g = []
2025-07-02 05:50:37.060 if alo < ahi:
2025-07-02 05:50:37.072 if blo < bhi:
2025-07-02 05:50:37.085 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:37.096 else:
2025-07-02 05:50:37.105 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:37.120 elif blo < bhi:
2025-07-02 05:50:37.133 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:37.145
2025-07-02 05:50:37.159 >       yield from g
2025-07-02 05:50:37.168
2025-07-02 05:50:37.175 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:37.182 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:37.196
2025-07-02 05:50:37.208 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:37.218 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:37.226 alo = 326, ahi = 1101
2025-07-02 05:50:37.235 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:37.243 blo = 326, bhi = 1101
2025-07-02 05:50:37.254
2025-07-02 05:50:37.263 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:37.271 r"""
2025-07-02 05:50:37.279 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:37.286 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:37.295 synch point, and intraline difference marking is done on the
2025-07-02 05:50:37.306 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:37.316
2025-07-02 05:50:37.324 Example:
2025-07-02 05:50:37.331
2025-07-02 05:50:37.343 >>> d = Differ()
2025-07-02 05:50:37.354 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:37.364 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:37.375 >>> print(''.join(results), end="")
2025-07-02 05:50:37.384 - abcDefghiJkl
2025-07-02 05:50:37.406 + abcdefGhijkl
2025-07-02 05:50:37.430 """
2025-07-02 05:50:37.442
2025-07-02 05:50:37.453 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:37.464 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:37.476 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:37.487 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:37.500 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:37.510
2025-07-02 05:50:37.519 # search for the pair that matches best without being identical
2025-07-02 05:50:37.526 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:37.532 # on junk -- unless we have to)
2025-07-02 05:50:37.540 for j in range(blo, bhi):
2025-07-02 05:50:37.546 bj = b[j]
2025-07-02 05:50:37.561 cruncher.set_seq2(bj)
2025-07-02 05:50:37.573 for i in range(alo, ahi):
2025-07-02 05:50:37.585 ai = a[i]
2025-07-02 05:50:37.596 if ai == bj:
2025-07-02 05:50:37.605 if eqi is None:
2025-07-02 05:50:37.612 eqi, eqj = i, j
2025-07-02 05:50:37.620 continue
2025-07-02 05:50:37.626 cruncher.set_seq1(ai)
2025-07-02 05:50:37.633 # computing similarity is expensive, so use the quick
2025-07-02 05:50:37.640 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:37.646 # compares by a factor of 3.
2025-07-02 05:50:37.654 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:37.665 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:37.676 # of the computation is cached by cruncher
2025-07-02 05:50:37.686 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:37.700 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:37.714 cruncher.ratio() > best_ratio:
2025-07-02 05:50:37.723 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:37.735 if best_ratio < cutoff:
2025-07-02 05:50:37.748 # no non-identical "pretty close" pair
2025-07-02 05:50:37.759 if eqi is None:
2025-07-02 05:50:37.769 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:37.777 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:37.784 return
2025-07-02 05:50:37.791 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:37.798 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:37.809 else:
2025-07-02 05:50:37.817 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:37.824 eqi = None
2025-07-02 05:50:37.830
2025-07-02 05:50:37.836 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:37.845 # identical
2025-07-02 05:50:37.856
2025-07-02 05:50:37.870 # pump out diffs from before the synch point
2025-07-02 05:50:37.881 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:37.894
2025-07-02 05:50:37.904 # do intraline marking on the synch pair
2025-07-02 05:50:37.917 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:37.929 if eqi is None:
2025-07-02 05:50:37.937 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:37.945 atags = btags = ""
2025-07-02 05:50:37.957 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:37.967 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:37.974 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:37.982 if tag == 'replace':
2025-07-02 05:50:37.989 atags += '^' * la
2025-07-02 05:50:37.998 btags += '^' * lb
2025-07-02 05:50:38.010 elif tag == 'delete':
2025-07-02 05:50:38.021 atags += '-' * la
2025-07-02 05:50:38.028 elif tag == 'insert':
2025-07-02 05:50:38.036 btags += '+' * lb
2025-07-02 05:50:38.043 elif tag == 'equal':
2025-07-02 05:50:38.051 atags += ' ' * la
2025-07-02 05:50:38.059 btags += ' ' * lb
2025-07-02 05:50:38.066 else:
2025-07-02 05:50:38.075 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:38.082 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:38.089 else:
2025-07-02 05:50:38.095 # the synch pair is identical
2025-07-02 05:50:38.102 yield '  ' + aelt
2025-07-02 05:50:38.109
2025-07-02 05:50:38.115 # pump out diffs from after the synch point
2025-07-02 05:50:38.122 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:38.132
2025-07-02 05:50:38.143 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:38.154 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:38.162
2025-07-02 05:50:38.168 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:38.174 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:38.181 alo = 327, ahi = 1101
2025-07-02 05:50:38.189 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:38.196 blo = 327, bhi = 1101
2025-07-02 05:50:38.204
2025-07-02 05:50:38.212 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:38.219 g = []
2025-07-02 05:50:38.226 if alo < ahi:
2025-07-02 05:50:38.232 if blo < bhi:
2025-07-02 05:50:38.238 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:38.247 else:
2025-07-02 05:50:38.262 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:38.270 elif blo < bhi:
2025-07-02 05:50:38.277 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:38.284
2025-07-02 05:50:38.292 >       yield from g
2025-07-02 05:50:38.299
2025-07-02 05:50:38.307 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:38.316 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:38.324
2025-07-02 05:50:38.336 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:38.346 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:38.354 alo = 327, ahi = 1101
2025-07-02 05:50:38.368 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:38.376 blo = 327, bhi = 1101
2025-07-02 05:50:38.383
2025-07-02 05:50:38.391 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:38.400 r"""
2025-07-02 05:50:38.411 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:38.421 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:38.428 synch point, and intraline difference marking is done on the
2025-07-02 05:50:38.434 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:38.440
2025-07-02 05:50:38.446 Example:
2025-07-02 05:50:38.453
2025-07-02 05:50:38.461 >>> d = Differ()
2025-07-02 05:50:38.469 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:38.481 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:38.491 >>> print(''.join(results), end="")
2025-07-02 05:50:38.498 - abcDefghiJkl
2025-07-02 05:50:38.513 + abcdefGhijkl
2025-07-02 05:50:38.528 """
2025-07-02 05:50:38.536
2025-07-02 05:50:38.544 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:38.551 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:38.559 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:38.567 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:38.575 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:38.581
2025-07-02 05:50:38.588 # search for the pair that matches best without being identical
2025-07-02 05:50:38.595 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:38.601 # on junk -- unless we have to)
2025-07-02 05:50:38.607 for j in range(blo, bhi):
2025-07-02 05:50:38.613 bj = b[j]
2025-07-02 05:50:38.620 cruncher.set_seq2(bj)
2025-07-02 05:50:38.629 for i in range(alo, ahi):
2025-07-02 05:50:38.636 ai = a[i]
2025-07-02 05:50:38.642 if ai == bj:
2025-07-02 05:50:38.650 if eqi is None:
2025-07-02 05:50:38.656 eqi, eqj = i, j
2025-07-02 05:50:38.669 continue
2025-07-02 05:50:38.684 cruncher.set_seq1(ai)
2025-07-02 05:50:38.694 # computing similarity is expensive, so use the quick
2025-07-02 05:50:38.704 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:38.712 # compares by a factor of 3.
2025-07-02 05:50:38.719 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:38.725 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:38.730 # of the computation is cached by cruncher
2025-07-02 05:50:38.736 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:38.741 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:38.747 cruncher.ratio() > best_ratio:
2025-07-02 05:50:38.753 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:38.758 if best_ratio < cutoff:
2025-07-02 05:50:38.768 # no non-identical "pretty close" pair
2025-07-02 05:50:38.776 if eqi is None:
2025-07-02 05:50:38.784 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:38.790 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:38.797 return
2025-07-02 05:50:38.802 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:38.809 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:38.815 else:
2025-07-02 05:50:38.831 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:38.841 eqi = None
2025-07-02 05:50:38.850
2025-07-02 05:50:38.858 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:38.866 # identical
2025-07-02 05:50:38.874
2025-07-02 05:50:38.882 # pump out diffs from before the synch point
2025-07-02 05:50:38.888 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:38.894
2025-07-02 05:50:38.903 # do intraline marking on the synch pair
2025-07-02 05:50:38.912 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:38.919 if eqi is None:
2025-07-02 05:50:38.926 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:38.934 atags = btags = ""
2025-07-02 05:50:38.941 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:38.947 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:38.954 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:38.962 if tag == 'replace':
2025-07-02 05:50:38.970 atags += '^' * la
2025-07-02 05:50:38.982 btags += '^' * lb
2025-07-02 05:50:38.992 elif tag == 'delete':
2025-07-02 05:50:38.999 atags += '-' * la
2025-07-02 05:50:39.010 elif tag == 'insert':
2025-07-02 05:50:39.018 btags += '+' * lb
2025-07-02 05:50:39.025 elif tag == 'equal':
2025-07-02 05:50:39.032 atags += ' ' * la
2025-07-02 05:50:39.040 btags += ' ' * lb
2025-07-02 05:50:39.047 else:
2025-07-02 05:50:39.060 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:39.073 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:39.086 else:
2025-07-02 05:50:39.094 # the synch pair is identical
2025-07-02 05:50:39.103 yield '  ' + aelt
2025-07-02 05:50:39.115
2025-07-02 05:50:39.127 # pump out diffs from after the synch point
2025-07-02 05:50:39.139 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:39.148
2025-07-02 05:50:39.161 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:39.172 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:39.179
2025-07-02 05:50:39.186 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:39.194 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:39.205 alo = 328, ahi = 1101
2025-07-02 05:50:39.216 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:39.225 blo = 328, bhi = 1101
2025-07-02 05:50:39.232
2025-07-02 05:50:39.239 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:39.245 g = []
2025-07-02 05:50:39.251 if alo < ahi:
2025-07-02 05:50:39.256 if blo < bhi:
2025-07-02 05:50:39.263 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:39.268 else:
2025-07-02 05:50:39.275 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:39.280 elif blo < bhi:
2025-07-02 05:50:39.287 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:39.293
2025-07-02 05:50:39.299 >       yield from g
2025-07-02 05:50:39.309
2025-07-02 05:50:39.319 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:39.326 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:39.335
2025-07-02 05:50:39.347 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:39.356 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:39.363 alo = 328, ahi = 1101
2025-07-02 05:50:39.371 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:39.381 blo = 328, bhi = 1101
2025-07-02 05:50:39.391
2025-07-02 05:50:39.400 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:39.407 r"""
2025-07-02 05:50:39.414 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:39.421 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:39.427 synch point, and intraline difference marking is done on the
2025-07-02 05:50:39.433 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:39.439
2025-07-02 05:50:39.444 Example:
2025-07-02 05:50:39.450
2025-07-02 05:50:39.456 >>> d = Differ()
2025-07-02 05:50:39.463 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:39.471 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:39.482 >>> print(''.join(results), end="")
2025-07-02 05:50:39.495 - abcDefghiJkl
2025-07-02 05:50:39.517 + abcdefGhijkl
2025-07-02 05:50:39.539 """
2025-07-02 05:50:39.547
2025-07-02 05:50:39.555 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:39.562 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:39.576 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:39.587 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:39.600 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:39.610
2025-07-02 05:50:39.618 # search for the pair that matches best without being identical
2025-07-02 05:50:39.626 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:39.632 # on junk -- unless we have to)
2025-07-02 05:50:39.638 for j in range(blo, bhi):
2025-07-02 05:50:39.645 bj = b[j]
2025-07-02 05:50:39.651 cruncher.set_seq2(bj)
2025-07-02 05:50:39.665 for i in range(alo, ahi):
2025-07-02 05:50:39.676 ai = a[i]
2025-07-02 05:50:39.688 if ai == bj:
2025-07-02 05:50:39.698 if eqi is None:
2025-07-02 05:50:39.709 eqi, eqj = i, j
2025-07-02 05:50:39.720 continue
2025-07-02 05:50:39.730 cruncher.set_seq1(ai)
2025-07-02 05:50:39.739 # computing similarity is expensive, so use the quick
2025-07-02 05:50:39.746 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:39.752 # compares by a factor of 3.
2025-07-02 05:50:39.762 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:39.773 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:39.782 # of the computation is cached by cruncher
2025-07-02 05:50:39.791 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:39.798 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:39.808 cruncher.ratio() > best_ratio:
2025-07-02 05:50:39.819 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:39.829 if best_ratio < cutoff:
2025-07-02 05:50:39.840 # no non-identical "pretty close" pair
2025-07-02 05:50:39.850 if eqi is None:
2025-07-02 05:50:39.859 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:39.868 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:39.875 return
2025-07-02 05:50:39.881 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:39.887 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:39.893 else:
2025-07-02 05:50:39.899 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:39.907 eqi = None
2025-07-02 05:50:39.918
2025-07-02 05:50:39.929 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:39.942 # identical
2025-07-02 05:50:39.955
2025-07-02 05:50:39.965 # pump out diffs from before the synch point
2025-07-02 05:50:39.981 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:39.991
2025-07-02 05:50:39.999 # do intraline marking on the synch pair
2025-07-02 05:50:40.007 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:40.013 if eqi is None:
2025-07-02 05:50:40.025 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:40.034 atags = btags = ""
2025-07-02 05:50:40.042 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:40.049 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:40.056 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:40.062 if tag == 'replace':
2025-07-02 05:50:40.070 atags += '^' * la
2025-07-02 05:50:40.079 btags += '^' * lb
2025-07-02 05:50:40.087 elif tag == 'delete':
2025-07-02 05:50:40.095 atags += '-' * la
2025-07-02 05:50:40.109 elif tag == 'insert':
2025-07-02 05:50:40.118 btags += '+' * lb
2025-07-02 05:50:40.126 elif tag == 'equal':
2025-07-02 05:50:40.132 atags += ' ' * la
2025-07-02 05:50:40.138 btags += ' ' * lb
2025-07-02 05:50:40.149 else:
2025-07-02 05:50:40.160 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:40.171 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:40.182 else:
2025-07-02 05:50:40.195 # the synch pair is identical
2025-07-02 05:50:40.206 yield '  ' + aelt
2025-07-02 05:50:40.217
2025-07-02 05:50:40.231 # pump out diffs from after the synch point
2025-07-02 05:50:40.243 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:40.256
2025-07-02 05:50:40.267 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:40.279 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:40.288
2025-07-02 05:50:40.296 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:40.304 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:40.310 alo = 329, ahi = 1101
2025-07-02 05:50:40.318 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:40.325 blo = 329, bhi = 1101
2025-07-02 05:50:40.331
2025-07-02 05:50:40.341 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:40.352 g = []
2025-07-02 05:50:40.360 if alo < ahi:
2025-07-02 05:50:40.368 if blo < bhi:
2025-07-02 05:50:40.375 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:40.381 else:
2025-07-02 05:50:40.387 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:40.394 elif blo < bhi:
2025-07-02 05:50:40.400 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:40.405
2025-07-02 05:50:40.411 >       yield from g
2025-07-02 05:50:40.417
2025-07-02 05:50:40.428 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:40.437 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:40.445
2025-07-02 05:50:40.451 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:40.459 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:40.467 alo = 329, ahi = 1101
2025-07-02 05:50:40.479 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:40.488 blo = 329, bhi = 1101
2025-07-02 05:50:40.495
2025-07-02 05:50:40.502 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:40.508 r"""
2025-07-02 05:50:40.514 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:40.520 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:40.526 synch point, and intraline difference marking is done on the
2025-07-02 05:50:40.537 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:40.548
2025-07-02 05:50:40.560 Example:
2025-07-02 05:50:40.571
2025-07-02 05:50:40.579 >>> d = Differ()
2025-07-02 05:50:40.587 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:40.594 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:40.599 >>> print(''.join(results), end="")
2025-07-02 05:50:40.607 - abcDefghiJkl
2025-07-02 05:50:40.628 + abcdefGhijkl
2025-07-02 05:50:40.643 """
2025-07-02 05:50:40.649
2025-07-02 05:50:40.655 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:40.661 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:40.667 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:40.673 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:40.687 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:40.700
2025-07-02 05:50:40.714 # search for the pair that matches best without being identical
2025-07-02 05:50:40.725 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:40.735 # on junk -- unless we have to)
2025-07-02 05:50:40.747 for j in range(blo, bhi):
2025-07-02 05:50:40.757 bj = b[j]
2025-07-02 05:50:40.764 cruncher.set_seq2(bj)
2025-07-02 05:50:40.771 for i in range(alo, ahi):
2025-07-02 05:50:40.785 ai = a[i]
2025-07-02 05:50:40.797 if ai == bj:
2025-07-02 05:50:40.806 if eqi is None:
2025-07-02 05:50:40.816 eqi, eqj = i, j
2025-07-02 05:50:40.826 continue
2025-07-02 05:50:40.833 cruncher.set_seq1(ai)
2025-07-02 05:50:40.839 # computing similarity is expensive, so use the quick
2025-07-02 05:50:40.846 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:40.852 # compares by a factor of 3.
2025-07-02 05:50:40.858 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:40.864 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:40.870 # of the computation is cached by cruncher
2025-07-02 05:50:40.877 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:40.885 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:40.892 cruncher.ratio() > best_ratio:
2025-07-02 05:50:40.900 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:40.907 if best_ratio < cutoff:
2025-07-02 05:50:40.915 # no non-identical "pretty close" pair
2025-07-02 05:50:40.922 if eqi is None:
2025-07-02 05:50:40.938 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:40.949 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:40.957 return
2025-07-02 05:50:40.964 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:40.970 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:40.976 else:
2025-07-02 05:50:40.984 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:40.993 eqi = None
2025-07-02 05:50:41.001
2025-07-02 05:50:41.013 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:41.020 # identical
2025-07-02 05:50:41.027
2025-07-02 05:50:41.040 # pump out diffs from before the synch point
2025-07-02 05:50:41.049 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:41.060
2025-07-02 05:50:41.070 # do intraline marking on the synch pair
2025-07-02 05:50:41.080 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:41.086 if eqi is None:
2025-07-02 05:50:41.094 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:41.106 atags = btags = ""
2025-07-02 05:50:41.117 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:41.128 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:41.139 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:41.149 if tag == 'replace':
2025-07-02 05:50:41.160 atags += '^' * la
2025-07-02 05:50:41.175 btags += '^' * lb
2025-07-02 05:50:41.182 elif tag == 'delete':
2025-07-02 05:50:41.191 atags += '-' * la
2025-07-02 05:50:41.204 elif tag == 'insert':
2025-07-02 05:50:41.214 btags += '+' * lb
2025-07-02 05:50:41.226 elif tag == 'equal':
2025-07-02 05:50:41.237 atags += ' ' * la
2025-07-02 05:50:41.245 btags += ' ' * lb
2025-07-02 05:50:41.252 else:
2025-07-02 05:50:41.259 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:41.266 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:41.277 else:
2025-07-02 05:50:41.288 # the synch pair is identical
2025-07-02 05:50:41.296 yield '  ' + aelt
2025-07-02 05:50:41.303
2025-07-02 05:50:41.311 # pump out diffs from after the synch point
2025-07-02 05:50:41.326 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:41.336
2025-07-02 05:50:41.344 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:41.351 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:41.359
2025-07-02 05:50:41.373 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:41.385 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:41.393 alo = 330, ahi = 1101
2025-07-02 05:50:41.402 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:41.414 blo = 330, bhi = 1101
2025-07-02 05:50:41.424
2025-07-02 05:50:41.431 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:41.439 g = []
2025-07-02 05:50:41.451 if alo < ahi:
2025-07-02 05:50:41.463 if blo < bhi:
2025-07-02 05:50:41.473 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:41.487 else:
2025-07-02 05:50:41.499 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:41.512 elif blo < bhi:
2025-07-02 05:50:41.525 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:41.538
2025-07-02 05:50:41.550 >       yield from g
2025-07-02 05:50:41.559
2025-07-02 05:50:41.569 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:41.577 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:41.588
2025-07-02 05:50:41.599 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:41.608 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:41.615 alo = 330, ahi = 1101
2025-07-02 05:50:41.622 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:41.627 blo = 330, bhi = 1101
2025-07-02 05:50:41.632
2025-07-02 05:50:41.638 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:41.646 r"""
2025-07-02 05:50:41.656 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:41.664 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:41.670 synch point, and intraline difference marking is done on the
2025-07-02 05:50:41.680 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:41.688
2025-07-02 05:50:41.695 Example:
2025-07-02 05:50:41.702
2025-07-02 05:50:41.714 >>> d = Differ()
2025-07-02 05:50:41.727 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:41.738 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:41.745 >>> print(''.join(results), end="")
2025-07-02 05:50:41.755 - abcDefghiJkl
2025-07-02 05:50:41.770 + abcdefGhijkl
2025-07-02 05:50:41.784 """
2025-07-02 05:50:41.791
2025-07-02 05:50:41.798 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:41.811 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:41.820 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:41.827 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:41.832 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:41.838
2025-07-02 05:50:41.845 # search for the pair that matches best without being identical
2025-07-02 05:50:41.852 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:41.858 # on junk -- unless we have to)
2025-07-02 05:50:41.863 for j in range(blo, bhi):
2025-07-02 05:50:41.868 bj = b[j]
2025-07-02 05:50:41.873 cruncher.set_seq2(bj)
2025-07-02 05:50:41.877 for i in range(alo, ahi):
2025-07-02 05:50:41.883 ai = a[i]
2025-07-02 05:50:41.888 if ai == bj:
2025-07-02 05:50:41.897 if eqi is None:
2025-07-02 05:50:41.905 eqi, eqj = i, j
2025-07-02 05:50:41.911 continue
2025-07-02 05:50:41.917 cruncher.set_seq1(ai)
2025-07-02 05:50:41.923 # computing similarity is expensive, so use the quick
2025-07-02 05:50:41.929 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:41.934 # compares by a factor of 3.
2025-07-02 05:50:41.940 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:41.947 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:41.953 # of the computation is cached by cruncher
2025-07-02 05:50:41.959 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:41.967 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:41.980 cruncher.ratio() > best_ratio:
2025-07-02 05:50:41.990 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:41.998 if best_ratio < cutoff:
2025-07-02 05:50:42.011 # no non-identical "pretty close" pair
2025-07-02 05:50:42.022 if eqi is None:
2025-07-02 05:50:42.030 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:42.042 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:42.053 return
2025-07-02 05:50:42.065 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:42.076 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:42.086 else:
2025-07-02 05:50:42.100 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:42.110 eqi = None
2025-07-02 05:50:42.118
2025-07-02 05:50:42.131 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:42.142 # identical
2025-07-02 05:50:42.151
2025-07-02 05:50:42.159 # pump out diffs from before the synch point
2025-07-02 05:50:42.171 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:42.180
2025-07-02 05:50:42.187 # do intraline marking on the synch pair
2025-07-02 05:50:42.194 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:42.203 if eqi is None:
2025-07-02 05:50:42.215 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:42.225 atags = btags = ""
2025-07-02 05:50:42.235 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:42.246 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:42.255 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:42.264 if tag == 'replace':
2025-07-02 05:50:42.272 atags += '^' * la
2025-07-02 05:50:42.278 btags += '^' * lb
2025-07-02 05:50:42.289 elif tag == 'delete':
2025-07-02 05:50:42.302 atags += '-' * la
2025-07-02 05:50:42.311 elif tag == 'insert':
2025-07-02 05:50:42.319 btags += '+' * lb
2025-07-02 05:50:42.333 elif tag == 'equal':
2025-07-02 05:50:42.344 atags += ' ' * la
2025-07-02 05:50:42.354 btags += ' ' * lb
2025-07-02 05:50:42.363 else:
2025-07-02 05:50:42.373 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:42.382 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:42.389 else:
2025-07-02 05:50:42.395 # the synch pair is identical
2025-07-02 05:50:42.403 yield '  ' + aelt
2025-07-02 05:50:42.415
2025-07-02 05:50:42.423 # pump out diffs from after the synch point
2025-07-02 05:50:42.430 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:42.440
2025-07-02 05:50:42.450 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:42.459 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:42.467
2025-07-02 05:50:42.474 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:42.483 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:42.494 alo = 331, ahi = 1101
2025-07-02 05:50:42.502 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:42.509 blo = 331, bhi = 1101
2025-07-02 05:50:42.515
2025-07-02 05:50:42.523 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:42.534 g = []
2025-07-02 05:50:42.543 if alo < ahi:
2025-07-02 05:50:42.550 if blo < bhi:
2025-07-02 05:50:42.558 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:42.565 else:
2025-07-02 05:50:42.571 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:42.579 elif blo < bhi:
2025-07-02 05:50:42.589 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:42.596
2025-07-02 05:50:42.602 >       yield from g
2025-07-02 05:50:42.614
2025-07-02 05:50:42.625 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:42.632 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:42.638
2025-07-02 05:50:42.649 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:42.659 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:42.667 alo = 331, ahi = 1101
2025-07-02 05:50:42.675 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:42.684 blo = 331, bhi = 1101
2025-07-02 05:50:42.693
2025-07-02 05:50:42.701 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:42.708 r"""
2025-07-02 05:50:42.714 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:42.721 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:42.727 synch point, and intraline difference marking is done on the
2025-07-02 05:50:42.739 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:42.749
2025-07-02 05:50:42.757 Example:
2025-07-02 05:50:42.764
2025-07-02 05:50:42.771 >>> d = Differ()
2025-07-02 05:50:42.779 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:42.791 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:42.800 >>> print(''.join(results), end="")
2025-07-02 05:50:42.806 - abcDefghiJkl
2025-07-02 05:50:42.830 + abcdefGhijkl
2025-07-02 05:50:42.855 """
2025-07-02 05:50:42.868
2025-07-02 05:50:42.881 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:42.893 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:42.903 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:42.912 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:42.918 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:42.925
2025-07-02 05:50:42.931 # search for the pair that matches best without being identical
2025-07-02 05:50:42.939 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:42.949 # on junk -- unless we have to)
2025-07-02 05:50:42.958 for j in range(blo, bhi):
2025-07-02 05:50:42.965 bj = b[j]
2025-07-02 05:50:42.976 cruncher.set_seq2(bj)
2025-07-02 05:50:42.986 for i in range(alo, ahi):
2025-07-02 05:50:42.998 ai = a[i]
2025-07-02 05:50:43.012 if ai == bj:
2025-07-02 05:50:43.023 if eqi is None:
2025-07-02 05:50:43.035 eqi, eqj = i, j
2025-07-02 05:50:43.044 continue
2025-07-02 05:50:43.052 cruncher.set_seq1(ai)
2025-07-02 05:50:43.064 # computing similarity is expensive, so use the quick
2025-07-02 05:50:43.075 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:43.084 # compares by a factor of 3.
2025-07-02 05:50:43.091 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:43.096 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:43.104 # of the computation is cached by cruncher
2025-07-02 05:50:43.115 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:43.123 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:43.130 cruncher.ratio() > best_ratio:
2025-07-02 05:50:43.140 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:43.150 if best_ratio < cutoff:
2025-07-02 05:50:43.161 # no non-identical "pretty close" pair
2025-07-02 05:50:43.173 if eqi is None:
2025-07-02 05:50:43.185 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:43.197 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:43.208 return
2025-07-02 05:50:43.219 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:43.229 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:43.242 else:
2025-07-02 05:50:43.253 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:43.262 eqi = None
2025-07-02 05:50:43.271
2025-07-02 05:50:43.284 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:43.293 # identical
2025-07-02 05:50:43.301
2025-07-02 05:50:43.312 # pump out diffs from before the synch point
2025-07-02 05:50:43.322 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:43.332
2025-07-02 05:50:43.343 # do intraline marking on the synch pair
2025-07-02 05:50:43.352 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:43.360 if eqi is None:
2025-07-02 05:50:43.373 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:43.385 atags = btags = ""
2025-07-02 05:50:43.398 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:43.408 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:43.416 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:43.423 if tag == 'replace':
2025-07-02 05:50:43.430 atags += '^' * la
2025-07-02 05:50:43.436 btags += '^' * lb
2025-07-02 05:50:43.443 elif tag == 'delete':
2025-07-02 05:50:43.452 atags += '-' * la
2025-07-02 05:50:43.462 elif tag == 'insert':
2025-07-02 05:50:43.471 btags += '+' * lb
2025-07-02 05:50:43.478 elif tag == 'equal':
2025-07-02 05:50:43.488 atags += ' ' * la
2025-07-02 05:50:43.498 btags += ' ' * lb
2025-07-02 05:50:43.510 else:
2025-07-02 05:50:43.525 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:43.540 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:43.549 else:
2025-07-02 05:50:43.556 # the synch pair is identical
2025-07-02 05:50:43.562 yield '  ' + aelt
2025-07-02 05:50:43.571
2025-07-02 05:50:43.581 # pump out diffs from after the synch point
2025-07-02 05:50:43.590 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:43.597
2025-07-02 05:50:43.602 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:43.608 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:43.617
2025-07-02 05:50:43.631 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:43.644 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:43.652 alo = 334, ahi = 1101
2025-07-02 05:50:43.666 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:43.677 blo = 334, bhi = 1101
2025-07-02 05:50:43.689
2025-07-02 05:50:43.699 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:43.710 g = []
2025-07-02 05:50:43.719 if alo < ahi:
2025-07-02 05:50:43.726 if blo < bhi:
2025-07-02 05:50:43.737 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:43.747 else:
2025-07-02 05:50:43.755 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:43.762 elif blo < bhi:
2025-07-02 05:50:43.771 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:43.782
2025-07-02 05:50:43.793 >       yield from g
2025-07-02 05:50:43.806
2025-07-02 05:50:43.816 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:43.826 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:43.833
2025-07-02 05:50:43.841 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:43.852 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:43.863 alo = 334, ahi = 1101
2025-07-02 05:50:43.873 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:43.881 blo = 334, bhi = 1101
2025-07-02 05:50:43.888
2025-07-02 05:50:43.894 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:43.907 r"""
2025-07-02 05:50:43.919 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:43.932 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:43.943 synch point, and intraline difference marking is done on the
2025-07-02 05:50:43.951 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:43.961
2025-07-02 05:50:43.972 Example:
2025-07-02 05:50:43.980
2025-07-02 05:50:43.989 >>> d = Differ()
2025-07-02 05:50:43.996 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:44.011 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:44.021 >>> print(''.join(results), end="")
2025-07-02 05:50:44.029 - abcDefghiJkl
2025-07-02 05:50:44.044 + abcdefGhijkl
2025-07-02 05:50:44.059 """
2025-07-02 05:50:44.067
2025-07-02 05:50:44.075 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:44.090 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:44.104 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:44.116 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:44.128 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:44.140
2025-07-02 05:50:44.152 # search for the pair that matches best without being identical
2025-07-02 05:50:44.161 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:44.173 # on junk -- unless we have to)
2025-07-02 05:50:44.182 for j in range(blo, bhi):
2025-07-02 05:50:44.195 bj = b[j]
2025-07-02 05:50:44.206 cruncher.set_seq2(bj)
2025-07-02 05:50:44.214 for i in range(alo, ahi):
2025-07-02 05:50:44.223 ai = a[i]
2025-07-02 05:50:44.230 if ai == bj:
2025-07-02 05:50:44.237 if eqi is None:
2025-07-02 05:50:44.243 eqi, eqj = i, j
2025-07-02 05:50:44.249 continue
2025-07-02 05:50:44.260 cruncher.set_seq1(ai)
2025-07-02 05:50:44.270 # computing similarity is expensive, so use the quick
2025-07-02 05:50:44.280 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:44.291 # compares by a factor of 3.
2025-07-02 05:50:44.302 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:44.311 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:44.318 # of the computation is cached by cruncher
2025-07-02 05:50:44.325 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:44.331 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:44.338 cruncher.ratio() > best_ratio:
2025-07-02 05:50:44.349 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:44.361 if best_ratio < cutoff:
2025-07-02 05:50:44.374 # no non-identical "pretty close" pair
2025-07-02 05:50:44.385 if eqi is None:
2025-07-02 05:50:44.395 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:44.404 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:44.413 return
2025-07-02 05:50:44.421 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:44.430 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:44.440 else:
2025-07-02 05:50:44.448 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:44.454 eqi = None
2025-07-02 05:50:44.465
2025-07-02 05:50:44.479 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:44.488 # identical
2025-07-02 05:50:44.501
2025-07-02 05:50:44.512 # pump out diffs from before the synch point
2025-07-02 05:50:44.523 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:44.531
2025-07-02 05:50:44.540 # do intraline marking on the synch pair
2025-07-02 05:50:44.552 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:44.561 if eqi is None:
2025-07-02 05:50:44.568 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:44.574 atags = btags = ""
2025-07-02 05:50:44.579 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:44.586 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:44.592 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:44.601 if tag == 'replace':
2025-07-02 05:50:44.608 atags += '^' * la
2025-07-02 05:50:44.615 btags += '^' * lb
2025-07-02 05:50:44.622 elif tag == 'delete':
2025-07-02 05:50:44.628 atags += '-' * la
2025-07-02 05:50:44.634 elif tag == 'insert':
2025-07-02 05:50:44.639 btags += '+' * lb
2025-07-02 05:50:44.646 elif tag == 'equal':
2025-07-02 05:50:44.652 atags += ' ' * la
2025-07-02 05:50:44.658 btags += ' ' * lb
2025-07-02 05:50:44.664 else:
2025-07-02 05:50:44.670 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:44.676 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:44.684 else:
2025-07-02 05:50:44.694 # the synch pair is identical
2025-07-02 05:50:44.702 yield '  ' + aelt
2025-07-02 05:50:44.710
2025-07-02 05:50:44.720 # pump out diffs from after the synch point
2025-07-02 05:50:44.733 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:44.745
2025-07-02 05:50:44.756 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:44.770 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:44.782
2025-07-02 05:50:44.795 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:44.808 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:44.822 alo = 335, ahi = 1101
2025-07-02 05:50:44.835 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:44.844 blo = 335, bhi = 1101
2025-07-02 05:50:44.852
2025-07-02 05:50:44.859 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:44.865 g = []
2025-07-02 05:50:44.871 if alo < ahi:
2025-07-02 05:50:44.878 if blo < bhi:
2025-07-02 05:50:44.887 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:44.897 else:
2025-07-02 05:50:44.905 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:44.911 elif blo < bhi:
2025-07-02 05:50:44.917 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:44.924
2025-07-02 05:50:44.931 >       yield from g
2025-07-02 05:50:44.938
2025-07-02 05:50:44.945 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:44.952 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:44.959
2025-07-02 05:50:44.966 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:44.972 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:44.978 alo = 335, ahi = 1101
2025-07-02 05:50:44.985 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:44.990 blo = 335, bhi = 1101
2025-07-02 05:50:44.995
2025-07-02 05:50:45.001 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:45.006 r"""
2025-07-02 05:50:45.013 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:45.025 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:45.034 synch point, and intraline difference marking is done on the
2025-07-02 05:50:45.041 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:45.047
2025-07-02 05:50:45.054 Example:
2025-07-02 05:50:45.066
2025-07-02 05:50:45.074 >>> d = Differ()
2025-07-02 05:50:45.084 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:45.096 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:45.106 >>> print(''.join(results), end="")
2025-07-02 05:50:45.112 - abcDefghiJkl
2025-07-02 05:50:45.125 + abcdefGhijkl
2025-07-02 05:50:45.136 """
2025-07-02 05:50:45.142
2025-07-02 05:50:45.147 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:45.153 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:45.160 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:45.166 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:45.172 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:45.179
2025-07-02 05:50:45.186 # search for the pair that matches best without being identical
2025-07-02 05:50:45.194 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:45.206 # on junk -- unless we have to)
2025-07-02 05:50:45.214 for j in range(blo, bhi):
2025-07-02 05:50:45.221 bj = b[j]
2025-07-02 05:50:45.229 cruncher.set_seq2(bj)
2025-07-02 05:50:45.237 for i in range(alo, ahi):
2025-07-02 05:50:45.244 ai = a[i]
2025-07-02 05:50:45.251 if ai == bj:
2025-07-02 05:50:45.259 if eqi is None:
2025-07-02 05:50:45.267 eqi, eqj = i, j
2025-07-02 05:50:45.278 continue
2025-07-02 05:50:45.287 cruncher.set_seq1(ai)
2025-07-02 05:50:45.294 # computing similarity is expensive, so use the quick
2025-07-02 05:50:45.299 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:45.304 # compares by a factor of 3.
2025-07-02 05:50:45.309 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:45.316 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:45.322 # of the computation is cached by cruncher
2025-07-02 05:50:45.329 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:45.336 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:45.343 cruncher.ratio() > best_ratio:
2025-07-02 05:50:45.350 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:45.360 if best_ratio < cutoff:
2025-07-02 05:50:45.374 # no non-identical "pretty close" pair
2025-07-02 05:50:45.381 if eqi is None:
2025-07-02 05:50:45.388 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:45.394 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:45.400 return
2025-07-02 05:50:45.407 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:45.414 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:45.423 else:
2025-07-02 05:50:45.431 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:45.438 eqi = None
2025-07-02 05:50:45.445
2025-07-02 05:50:45.451 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:45.458 # identical
2025-07-02 05:50:45.471
2025-07-02 05:50:45.480 # pump out diffs from before the synch point
2025-07-02 05:50:45.486 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:45.491
2025-07-02 05:50:45.496 # do intraline marking on the synch pair
2025-07-02 05:50:45.502 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:45.507 if eqi is None:
2025-07-02 05:50:45.513 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:45.520 atags = btags = ""
2025-07-02 05:50:45.528 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:45.535 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:45.543 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:45.554 if tag == 'replace':
2025-07-02 05:50:45.563 atags += '^' * la
2025-07-02 05:50:45.572 btags += '^' * lb
2025-07-02 05:50:45.578 elif tag == 'delete':
2025-07-02 05:50:45.584 atags += '-' * la
2025-07-02 05:50:45.590 elif tag == 'insert':
2025-07-02 05:50:45.596 btags += '+' * lb
2025-07-02 05:50:45.602 elif tag == 'equal':
2025-07-02 05:50:45.610 atags += ' ' * la
2025-07-02 05:50:45.619 btags += ' ' * lb
2025-07-02 05:50:45.626 else:
2025-07-02 05:50:45.632 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:45.636 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:45.641 else:
2025-07-02 05:50:45.647 # the synch pair is identical
2025-07-02 05:50:45.653 yield '  ' + aelt
2025-07-02 05:50:45.663
2025-07-02 05:50:45.670 # pump out diffs from after the synch point
2025-07-02 05:50:45.676 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:45.681
2025-07-02 05:50:45.686 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:45.692 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:45.699
2025-07-02 05:50:45.706 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:45.713 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:45.720 alo = 336, ahi = 1101
2025-07-02 05:50:45.729 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:45.737 blo = 336, bhi = 1101
2025-07-02 05:50:45.744
2025-07-02 05:50:45.751 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:45.759 g = []
2025-07-02 05:50:45.766 if alo < ahi:
2025-07-02 05:50:45.774 if blo < bhi:
2025-07-02 05:50:45.781 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:45.793 else:
2025-07-02 05:50:45.802 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:45.809 elif blo < bhi:
2025-07-02 05:50:45.816 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:45.822
2025-07-02 05:50:45.827 >       yield from g
2025-07-02 05:50:45.831
2025-07-02 05:50:45.837 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:45.843 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:45.851
2025-07-02 05:50:45.859 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:45.872 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:45.879 alo = 336, ahi = 1101
2025-07-02 05:50:45.886 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:45.896 blo = 336, bhi = 1101
2025-07-02 05:50:45.907
2025-07-02 05:50:45.916 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:45.924 r"""
2025-07-02 05:50:45.931 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:45.937 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:45.944 synch point, and intraline difference marking is done on the
2025-07-02 05:50:45.949 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:45.954
2025-07-02 05:50:45.958 Example:
2025-07-02 05:50:45.963
2025-07-02 05:50:45.968 >>> d = Differ()
2025-07-02 05:50:45.973 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:45.978 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:45.983 >>> print(''.join(results), end="")
2025-07-02 05:50:45.988 - abcDefghiJkl
2025-07-02 05:50:45.998 + abcdefGhijkl
2025-07-02 05:50:46.010 """
2025-07-02 05:50:46.017
2025-07-02 05:50:46.024 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:46.031 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:46.039 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:46.047 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:46.054 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:46.062
2025-07-02 05:50:46.075 # search for the pair that matches best without being identical
2025-07-02 05:50:46.085 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:46.091 # on junk -- unless we have to)
2025-07-02 05:50:46.097 for j in range(blo, bhi):
2025-07-02 05:50:46.102 bj = b[j]
2025-07-02 05:50:46.107 cruncher.set_seq2(bj)
2025-07-02 05:50:46.112 for i in range(alo, ahi):
2025-07-02 05:50:46.118 ai = a[i]
2025-07-02 05:50:46.123 if ai == bj:
2025-07-02 05:50:46.129 if eqi is None:
2025-07-02 05:50:46.139 eqi, eqj = i, j
2025-07-02 05:50:46.149 continue
2025-07-02 05:50:46.157 cruncher.set_seq1(ai)
2025-07-02 05:50:46.163 # computing similarity is expensive, so use the quick
2025-07-02 05:50:46.176 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:46.188 # compares by a factor of 3.
2025-07-02 05:50:46.200 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:46.211 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:46.221 # of the computation is cached by cruncher
2025-07-02 05:50:46.232 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:46.242 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:46.255 cruncher.ratio() > best_ratio:
2025-07-02 05:50:46.267 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:46.277 if best_ratio < cutoff:
2025-07-02 05:50:46.289 # no non-identical "pretty close" pair
2025-07-02 05:50:46.301 if eqi is None:
2025-07-02 05:50:46.310 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:46.319 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:46.326 return
2025-07-02 05:50:46.336 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:46.344 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:46.351 else:
2025-07-02 05:50:46.359 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:46.366 eqi = None
2025-07-02 05:50:46.378
2025-07-02 05:50:46.386 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:46.393 # identical
2025-07-02 05:50:46.399
2025-07-02 05:50:46.406 # pump out diffs from before the synch point
2025-07-02 05:50:46.415 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:46.423
2025-07-02 05:50:46.429 # do intraline marking on the synch pair
2025-07-02 05:50:46.435 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:46.441 if eqi is None:
2025-07-02 05:50:46.448 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:46.454 atags = btags = ""
2025-07-02 05:50:46.463 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:46.475 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:46.483 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:46.491 if tag == 'replace':
2025-07-02 05:50:46.499 atags += '^' * la
2025-07-02 05:50:46.506 btags += '^' * lb
2025-07-02 05:50:46.514 elif tag == 'delete':
2025-07-02 05:50:46.525 atags += '-' * la
2025-07-02 05:50:46.533 elif tag == 'insert':
2025-07-02 05:50:46.542 btags += '+' * lb
2025-07-02 05:50:46.548 elif tag == 'equal':
2025-07-02 05:50:46.554 atags += ' ' * la
2025-07-02 05:50:46.560 btags += ' ' * lb
2025-07-02 05:50:46.566 else:
2025-07-02 05:50:46.575 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:46.582 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:46.587 else:
2025-07-02 05:50:46.593 # the synch pair is identical
2025-07-02 05:50:46.601 yield '  ' + aelt
2025-07-02 05:50:46.615
2025-07-02 05:50:46.625 # pump out diffs from after the synch point
2025-07-02 05:50:46.631 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:46.638
2025-07-02 05:50:46.646 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:46.653 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:46.660
2025-07-02 05:50:46.666 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:46.675 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:46.682 alo = 337, ahi = 1101
2025-07-02 05:50:46.691 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:46.698 blo = 337, bhi = 1101
2025-07-02 05:50:46.710
2025-07-02 05:50:46.720 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:46.728 g = []
2025-07-02 05:50:46.741 if alo < ahi:
2025-07-02 05:50:46.751 if blo < bhi:
2025-07-02 05:50:46.759 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:46.770 else:
2025-07-02 05:50:46.781 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:46.796 elif blo < bhi:
2025-07-02 05:50:46.807 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:46.815
2025-07-02 05:50:46.823 >       yield from g
2025-07-02 05:50:46.831
2025-07-02 05:50:46.842 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:46.850 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:46.859
2025-07-02 05:50:46.871 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:46.885 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:46.896 alo = 337, ahi = 1101
2025-07-02 05:50:46.909 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:46.920 blo = 337, bhi = 1101
2025-07-02 05:50:46.932
2025-07-02 05:50:46.944 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:46.956 r"""
2025-07-02 05:50:46.968 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:46.981 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:46.993 synch point, and intraline difference marking is done on the
2025-07-02 05:50:47.006 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:47.015
2025-07-02 05:50:47.022 Example:
2025-07-02 05:50:47.028
2025-07-02 05:50:47.035 >>> d = Differ()
2025-07-02 05:50:47.043 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:47.053 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:47.061 >>> print(''.join(results), end="")
2025-07-02 05:50:47.069 - abcDefghiJkl
2025-07-02 05:50:47.082 + abcdefGhijkl
2025-07-02 05:50:47.108 """
2025-07-02 05:50:47.119
2025-07-02 05:50:47.133 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:47.143 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:47.151 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:47.164 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:47.172 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:47.178
2025-07-02 05:50:47.184 # search for the pair that matches best without being identical
2025-07-02 05:50:47.189 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:47.194 # on junk -- unless we have to)
2025-07-02 05:50:47.199 for j in range(blo, bhi):
2025-07-02 05:50:47.208 bj = b[j]
2025-07-02 05:50:47.219 cruncher.set_seq2(bj)
2025-07-02 05:50:47.234 for i in range(alo, ahi):
2025-07-02 05:50:47.246 ai = a[i]
2025-07-02 05:50:47.258 if ai == bj:
2025-07-02 05:50:47.269 if eqi is None:
2025-07-02 05:50:47.283 eqi, eqj = i, j
2025-07-02 05:50:47.297 continue
2025-07-02 05:50:47.305 cruncher.set_seq1(ai)
2025-07-02 05:50:47.317 # computing similarity is expensive, so use the quick
2025-07-02 05:50:47.327 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:47.340 # compares by a factor of 3.
2025-07-02 05:50:47.353 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:47.365 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:47.374 # of the computation is cached by cruncher
2025-07-02 05:50:47.386 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:47.394 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:47.401 cruncher.ratio() > best_ratio:
2025-07-02 05:50:47.407 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:47.413 if best_ratio < cutoff:
2025-07-02 05:50:47.418 # no non-identical "pretty close" pair
2025-07-02 05:50:47.424 if eqi is None:
2025-07-02 05:50:47.429 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:47.436 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:47.443 return
2025-07-02 05:50:47.450 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:47.457 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:47.464 else:
2025-07-02 05:50:47.470 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:47.479 eqi = None
2025-07-02 05:50:47.491
2025-07-02 05:50:47.500 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:47.506 # identical
2025-07-02 05:50:47.512
2025-07-02 05:50:47.518 # pump out diffs from before the synch point
2025-07-02 05:50:47.531 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:47.542
2025-07-02 05:50:47.551 # do intraline marking on the synch pair
2025-07-02 05:50:47.559 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:47.571 if eqi is None:
2025-07-02 05:50:47.581 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:47.594 atags = btags = ""
2025-07-02 05:50:47.606 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:47.616 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:47.624 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:47.630 if tag == 'replace':
2025-07-02 05:50:47.637 atags += '^' * la
2025-07-02 05:50:47.643 btags += '^' * lb
2025-07-02 05:50:47.651 elif tag == 'delete':
2025-07-02 05:50:47.663 atags += '-' * la
2025-07-02 05:50:47.672 elif tag == 'insert':
2025-07-02 05:50:47.678 btags += '+' * lb
2025-07-02 05:50:47.683 elif tag == 'equal':
2025-07-02 05:50:47.690 atags += ' ' * la
2025-07-02 05:50:47.696 btags += ' ' * lb
2025-07-02 05:50:47.710 else:
2025-07-02 05:50:47.720 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:47.728 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:47.735 else:
2025-07-02 05:50:47.741 # the synch pair is identical
2025-07-02 05:50:47.747 yield '  ' + aelt
2025-07-02 05:50:47.753
2025-07-02 05:50:47.759 # pump out diffs from after the synch point
2025-07-02 05:50:47.767 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:47.777
2025-07-02 05:50:47.785 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:47.791 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:47.796
2025-07-02 05:50:47.802 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:47.809 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:47.816 alo = 338, ahi = 1101
2025-07-02 05:50:47.823 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:47.829 blo = 338, bhi = 1101
2025-07-02 05:50:47.833
2025-07-02 05:50:47.838 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:47.843 g = []
2025-07-02 05:50:47.847 if alo < ahi:
2025-07-02 05:50:47.852 if blo < bhi:
2025-07-02 05:50:47.864 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:47.870 else:
2025-07-02 05:50:47.877 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:47.885 elif blo < bhi:
2025-07-02 05:50:47.892 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:47.899
2025-07-02 05:50:47.906 >       yield from g
2025-07-02 05:50:47.914
2025-07-02 05:50:47.927 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:47.936 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:47.943
2025-07-02 05:50:47.949 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:47.959 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:47.966 alo = 338, ahi = 1101
2025-07-02 05:50:47.975 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:47.982 blo = 338, bhi = 1101
2025-07-02 05:50:47.991
2025-07-02 05:50:48.003 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:48.016 r"""
2025-07-02 05:50:48.029 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:48.042 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:48.053 synch point, and intraline difference marking is done on the
2025-07-02 05:50:48.065 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:48.077
2025-07-02 05:50:48.090 Example:
2025-07-02 05:50:48.101
2025-07-02 05:50:48.113 >>> d = Differ()
2025-07-02 05:50:48.127 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:48.141 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:48.154 >>> print(''.join(results), end="")
2025-07-02 05:50:48.163 - abcDefghiJkl
2025-07-02 05:50:48.178 + abcdefGhijkl
2025-07-02 05:50:48.194 """
2025-07-02 05:50:48.205
2025-07-02 05:50:48.215 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:48.223 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:48.229 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:48.235 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:48.242 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:48.248
2025-07-02 05:50:48.254 # search for the pair that matches best without being identical
2025-07-02 05:50:48.261 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:48.270 # on junk -- unless we have to)
2025-07-02 05:50:48.280 for j in range(blo, bhi):
2025-07-02 05:50:48.291 bj = b[j]
2025-07-02 05:50:48.300 cruncher.set_seq2(bj)
2025-07-02 05:50:48.307 for i in range(alo, ahi):
2025-07-02 05:50:48.316 ai = a[i]
2025-07-02 05:50:48.327 if ai == bj:
2025-07-02 05:50:48.336 if eqi is None:
2025-07-02 05:50:48.343 eqi, eqj = i, j
2025-07-02 05:50:48.351 continue
2025-07-02 05:50:48.363 cruncher.set_seq1(ai)
2025-07-02 05:50:48.376 # computing similarity is expensive, so use the quick
2025-07-02 05:50:48.387 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:48.396 # compares by a factor of 3.
2025-07-02 05:50:48.403 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:48.409 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:48.414 # of the computation is cached by cruncher
2025-07-02 05:50:48.421 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:48.426 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:48.432 cruncher.ratio() > best_ratio:
2025-07-02 05:50:48.437 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:48.442 if best_ratio < cutoff:
2025-07-02 05:50:48.448 # no non-identical "pretty close" pair
2025-07-02 05:50:48.453 if eqi is None:
2025-07-02 05:50:48.458 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:48.463 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:48.468 return
2025-07-02 05:50:48.474 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:48.479 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:48.485 else:
2025-07-02 05:50:48.490 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:48.495 eqi = None
2025-07-02 05:50:48.501
2025-07-02 05:50:48.506 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:48.512 # identical
2025-07-02 05:50:48.519
2025-07-02 05:50:48.525 # pump out diffs from before the synch point
2025-07-02 05:50:48.532 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:48.539
2025-07-02 05:50:48.547 # do intraline marking on the synch pair
2025-07-02 05:50:48.559 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:48.568 if eqi is None:
2025-07-02 05:50:48.578 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:48.587 atags = btags = ""
2025-07-02 05:50:48.595 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:48.603 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:48.609 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:48.616 if tag == 'replace':
2025-07-02 05:50:48.623 atags += '^' * la
2025-07-02 05:50:48.630 btags += '^' * lb
2025-07-02 05:50:48.637 elif tag == 'delete':
2025-07-02 05:50:48.642 atags += '-' * la
2025-07-02 05:50:48.652 elif tag == 'insert':
2025-07-02 05:50:48.663 btags += '+' * lb
2025-07-02 05:50:48.671 elif tag == 'equal':
2025-07-02 05:50:48.680 atags += ' ' * la
2025-07-02 05:50:48.690 btags += ' ' * lb
2025-07-02 05:50:48.703 else:
2025-07-02 05:50:48.717 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:48.730 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:48.741 else:
2025-07-02 05:50:48.751 # the synch pair is identical
2025-07-02 05:50:48.761 yield '  ' + aelt
2025-07-02 05:50:48.770
2025-07-02 05:50:48.783 # pump out diffs from after the synch point
2025-07-02 05:50:48.794 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:48.805
2025-07-02 05:50:48.817 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:48.830 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:48.841
2025-07-02 05:50:48.851 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:48.863 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:48.870 alo = 339, ahi = 1101
2025-07-02 05:50:48.879 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:48.887 blo = 339, bhi = 1101
2025-07-02 05:50:48.898
2025-07-02 05:50:48.908 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:48.916 g = []
2025-07-02 05:50:48.923 if alo < ahi:
2025-07-02 05:50:48.931 if blo < bhi:
2025-07-02 05:50:48.941 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:48.951 else:
2025-07-02 05:50:48.959 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:48.969 elif blo < bhi:
2025-07-02 05:50:48.977 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:48.986
2025-07-02 05:50:48.995 >       yield from g
2025-07-02 05:50:49.003
2025-07-02 05:50:49.011 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:49.023 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:49.032
2025-07-02 05:50:49.040 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:49.052 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:49.061 alo = 339, ahi = 1101
2025-07-02 05:50:49.068 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:49.075 blo = 339, bhi = 1101
2025-07-02 05:50:49.081
2025-07-02 05:50:49.093 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:49.101 r"""
2025-07-02 05:50:49.107 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:49.118 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:49.128 synch point, and intraline difference marking is done on the
2025-07-02 05:50:49.135 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:49.143
2025-07-02 05:50:49.151 Example:
2025-07-02 05:50:49.162
2025-07-02 05:50:49.170 >>> d = Differ()
2025-07-02 05:50:49.179 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:49.187 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:49.194 >>> print(''.join(results), end="")
2025-07-02 05:50:49.201 - abcDefghiJkl
2025-07-02 05:50:49.223 + abcdefGhijkl
2025-07-02 05:50:49.241 """
2025-07-02 05:50:49.249
2025-07-02 05:50:49.257 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:49.264 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:49.270 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:49.276 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:49.283 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:49.291
2025-07-02 05:50:49.299 # search for the pair that matches best without being identical
2025-07-02 05:50:49.310 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:49.319 # on junk -- unless we have to)
2025-07-02 05:50:49.333 for j in range(blo, bhi):
2025-07-02 05:50:49.342 bj = b[j]
2025-07-02 05:50:49.355 cruncher.set_seq2(bj)
2025-07-02 05:50:49.364 for i in range(alo, ahi):
2025-07-02 05:50:49.372 ai = a[i]
2025-07-02 05:50:49.378 if ai == bj:
2025-07-02 05:50:49.385 if eqi is None:
2025-07-02 05:50:49.390 eqi, eqj = i, j
2025-07-02 05:50:49.397 continue
2025-07-02 05:50:49.405 cruncher.set_seq1(ai)
2025-07-02 05:50:49.412 # computing similarity is expensive, so use the quick
2025-07-02 05:50:49.418 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:49.424 # compares by a factor of 3.
2025-07-02 05:50:49.431 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:49.438 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:49.444 # of the computation is cached by cruncher
2025-07-02 05:50:49.450 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:49.457 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:49.470 cruncher.ratio() > best_ratio:
2025-07-02 05:50:49.479 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:49.487 if best_ratio < cutoff:
2025-07-02 05:50:49.495 # no non-identical "pretty close" pair
2025-07-02 05:50:49.501 if eqi is None:
2025-07-02 05:50:49.508 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:49.517 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:49.524 return
2025-07-02 05:50:49.532 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:49.540 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:49.547 else:
2025-07-02 05:50:49.555 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:49.562 eqi = None
2025-07-02 05:50:49.568
2025-07-02 05:50:49.579 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:49.588 # identical
2025-07-02 05:50:49.594
2025-07-02 05:50:49.599 # pump out diffs from before the synch point
2025-07-02 05:50:49.605 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:49.611
2025-07-02 05:50:49.619 # do intraline marking on the synch pair
2025-07-02 05:50:49.627 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:49.634 if eqi is None:
2025-07-02 05:50:49.643 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:49.650 atags = btags = ""
2025-07-02 05:50:49.661 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:49.670 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:49.677 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:49.686 if tag == 'replace':
2025-07-02 05:50:49.696 atags += '^' * la
2025-07-02 05:50:49.704 btags += '^' * lb
2025-07-02 05:50:49.711 elif tag == 'delete':
2025-07-02 05:50:49.717 atags += '-' * la
2025-07-02 05:50:49.723 elif tag == 'insert':
2025-07-02 05:50:49.729 btags += '+' * lb
2025-07-02 05:50:49.736 elif tag == 'equal':
2025-07-02 05:50:49.743 atags += ' ' * la
2025-07-02 05:50:49.751 btags += ' ' * lb
2025-07-02 05:50:49.759 else:
2025-07-02 05:50:49.768 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:49.775 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:49.782 else:
2025-07-02 05:50:49.791 # the synch pair is identical
2025-07-02 05:50:49.800 yield '  ' + aelt
2025-07-02 05:50:49.813
2025-07-02 05:50:49.823 # pump out diffs from after the synch point
2025-07-02 05:50:49.831 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:49.845
2025-07-02 05:50:49.853 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:49.864 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:49.874
2025-07-02 05:50:49.885 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:49.895 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:49.907 alo = 340, ahi = 1101
2025-07-02 05:50:49.916 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:49.923 blo = 340, bhi = 1101
2025-07-02 05:50:49.932
2025-07-02 05:50:49.939 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:49.946 g = []
2025-07-02 05:50:49.951 if alo < ahi:
2025-07-02 05:50:49.959 if blo < bhi:
2025-07-02 05:50:49.966 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:49.971 else:
2025-07-02 05:50:49.983 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:49.994 elif blo < bhi:
2025-07-02 05:50:50.000 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:50.006
2025-07-02 05:50:50.013 >       yield from g
2025-07-02 05:50:50.021
2025-07-02 05:50:50.027 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:50.033 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:50.046
2025-07-02 05:50:50.053 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:50.062 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:50.071 alo = 340, ahi = 1101
2025-07-02 05:50:50.082 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:50.091 blo = 340, bhi = 1101
2025-07-02 05:50:50.099
2025-07-02 05:50:50.106 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:50.114 r"""
2025-07-02 05:50:50.124 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:50.132 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:50.140 synch point, and intraline difference marking is done on the
2025-07-02 05:50:50.148 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:50.155
2025-07-02 05:50:50.169 Example:
2025-07-02 05:50:50.180
2025-07-02 05:50:50.189 >>> d = Differ()
2025-07-02 05:50:50.199 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:50.211 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:50.222 >>> print(''.join(results), end="")
2025-07-02 05:50:50.239 - abcDefghiJkl
2025-07-02 05:50:50.259 + abcdefGhijkl
2025-07-02 05:50:50.273 """
2025-07-02 05:50:50.279
2025-07-02 05:50:50.285 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:50.291 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:50.297 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:50.304 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:50.311 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:50.318
2025-07-02 05:50:50.331 # search for the pair that matches best without being identical
2025-07-02 05:50:50.338 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:50.345 # on junk -- unless we have to)
2025-07-02 05:50:50.351 for j in range(blo, bhi):
2025-07-02 05:50:50.357 bj = b[j]
2025-07-02 05:50:50.364 cruncher.set_seq2(bj)
2025-07-02 05:50:50.371 for i in range(alo, ahi):
2025-07-02 05:50:50.382 ai = a[i]
2025-07-02 05:50:50.394 if ai == bj:
2025-07-02 05:50:50.411 if eqi is None:
2025-07-02 05:50:50.423 eqi, eqj = i, j
2025-07-02 05:50:50.432 continue
2025-07-02 05:50:50.440 cruncher.set_seq1(ai)
2025-07-02 05:50:50.452 # computing similarity is expensive, so use the quick
2025-07-02 05:50:50.465 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:50.477 # compares by a factor of 3.
2025-07-02 05:50:50.489 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:50.499 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:50.508 # of the computation is cached by cruncher
2025-07-02 05:50:50.516 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:50.523 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:50.532 cruncher.ratio() > best_ratio:
2025-07-02 05:50:50.539 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:50.547 if best_ratio < cutoff:
2025-07-02 05:50:50.556 # no non-identical "pretty close" pair
2025-07-02 05:50:50.566 if eqi is None:
2025-07-02 05:50:50.576 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:50.584 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:50.591 return
2025-07-02 05:50:50.599 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:50.610 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:50.618 else:
2025-07-02 05:50:50.625 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:50.634 eqi = None
2025-07-02 05:50:50.643
2025-07-02 05:50:50.651 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:50.657 # identical
2025-07-02 05:50:50.662
2025-07-02 05:50:50.675 # pump out diffs from before the synch point
2025-07-02 05:50:50.685 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:50.695
2025-07-02 05:50:50.705 # do intraline marking on the synch pair
2025-07-02 05:50:50.715 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:50.722 if eqi is None:
2025-07-02 05:50:50.734 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:50.745 atags = btags = ""
2025-07-02 05:50:50.755 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:50.767 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:50.777 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:50.790 if tag == 'replace':
2025-07-02 05:50:50.801 atags += '^' * la
2025-07-02 05:50:50.814 btags += '^' * lb
2025-07-02 05:50:50.824 elif tag == 'delete':
2025-07-02 05:50:50.836 atags += '-' * la
2025-07-02 05:50:50.847 elif tag == 'insert':
2025-07-02 05:50:50.856 btags += '+' * lb
2025-07-02 05:50:50.865 elif tag == 'equal':
2025-07-02 05:50:50.872 atags += ' ' * la
2025-07-02 05:50:50.879 btags += ' ' * lb
2025-07-02 05:50:50.890 else:
2025-07-02 05:50:50.899 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:50.907 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:50.916 else:
2025-07-02 05:50:50.928 # the synch pair is identical
2025-07-02 05:50:50.936 yield '  ' + aelt
2025-07-02 05:50:50.944
2025-07-02 05:50:50.959 # pump out diffs from after the synch point
2025-07-02 05:50:50.967 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:50.974
2025-07-02 05:50:50.983 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:50.994 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:51.007
2025-07-02 05:50:51.019 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:51.033 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:51.045 alo = 341, ahi = 1101
2025-07-02 05:50:51.060 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:51.071 blo = 341, bhi = 1101
2025-07-02 05:50:51.081
2025-07-02 05:50:51.089 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:51.096 g = []
2025-07-02 05:50:51.101 if alo < ahi:
2025-07-02 05:50:51.107 if blo < bhi:
2025-07-02 05:50:51.113 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:51.125 else:
2025-07-02 05:50:51.135 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:51.142 elif blo < bhi:
2025-07-02 05:50:51.152 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:51.161
2025-07-02 05:50:51.169 >       yield from g
2025-07-02 05:50:51.176
2025-07-02 05:50:51.181 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:51.186 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:51.190
2025-07-02 05:50:51.195 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:51.200 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:51.204 alo = 341, ahi = 1101
2025-07-02 05:50:51.209 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:51.215 blo = 341, bhi = 1101
2025-07-02 05:50:51.221
2025-07-02 05:50:51.227 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:51.235 r"""
2025-07-02 05:50:51.240 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:51.246 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:51.252 synch point, and intraline difference marking is done on the
2025-07-02 05:50:51.256 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:51.261
2025-07-02 05:50:51.265 Example:
2025-07-02 05:50:51.270
2025-07-02 05:50:51.277 >>> d = Differ()
2025-07-02 05:50:51.283 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:51.290 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:51.298 >>> print(''.join(results), end="")
2025-07-02 05:50:51.305 - abcDefghiJkl
2025-07-02 05:50:51.317 + abcdefGhijkl
2025-07-02 05:50:51.337 """
2025-07-02 05:50:51.346
2025-07-02 05:50:51.353 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:51.359 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:51.365 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:51.370 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:51.377 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:51.383
2025-07-02 05:50:51.389 # search for the pair that matches best without being identical
2025-07-02 05:50:51.396 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:51.402 # on junk -- unless we have to)
2025-07-02 05:50:51.408 for j in range(blo, bhi):
2025-07-02 05:50:51.414 bj = b[j]
2025-07-02 05:50:51.420 cruncher.set_seq2(bj)
2025-07-02 05:50:51.427 for i in range(alo, ahi):
2025-07-02 05:50:51.434 ai = a[i]
2025-07-02 05:50:51.439 if ai == bj:
2025-07-02 05:50:51.446 if eqi is None:
2025-07-02 05:50:51.452 eqi, eqj = i, j
2025-07-02 05:50:51.465 continue
2025-07-02 05:50:51.474 cruncher.set_seq1(ai)
2025-07-02 05:50:51.483 # computing similarity is expensive, so use the quick
2025-07-02 05:50:51.494 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:51.502 # compares by a factor of 3.
2025-07-02 05:50:51.508 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:51.514 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:51.518 # of the computation is cached by cruncher
2025-07-02 05:50:51.525 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:51.531 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:51.539 cruncher.ratio() > best_ratio:
2025-07-02 05:50:51.546 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:51.556 if best_ratio < cutoff:
2025-07-02 05:50:51.566 # no non-identical "pretty close" pair
2025-07-02 05:50:51.574 if eqi is None:
2025-07-02 05:50:51.580 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:51.586 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:51.591 return
2025-07-02 05:50:51.596 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:51.602 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:51.608 else:
2025-07-02 05:50:51.614 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:51.624 eqi = None
2025-07-02 05:50:51.634
2025-07-02 05:50:51.644 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:51.651 # identical
2025-07-02 05:50:51.657
2025-07-02 05:50:51.670 # pump out diffs from before the synch point
2025-07-02 05:50:51.681 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:51.691
2025-07-02 05:50:51.698 # do intraline marking on the synch pair
2025-07-02 05:50:51.712 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:51.722 if eqi is None:
2025-07-02 05:50:51.734 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:51.745 atags = btags = ""
2025-07-02 05:50:51.755 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:51.767 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:51.777 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:51.785 if tag == 'replace':
2025-07-02 05:50:51.797 atags += '^' * la
2025-07-02 05:50:51.806 btags += '^' * lb
2025-07-02 05:50:51.814 elif tag == 'delete':
2025-07-02 05:50:51.826 atags += '-' * la
2025-07-02 05:50:51.835 elif tag == 'insert':
2025-07-02 05:50:51.844 btags += '+' * lb
2025-07-02 05:50:51.856 elif tag == 'equal':
2025-07-02 05:50:51.865 atags += ' ' * la
2025-07-02 05:50:51.872 btags += ' ' * lb
2025-07-02 05:50:51.878 else:
2025-07-02 05:50:51.889 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:51.899 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:51.908 else:
2025-07-02 05:50:51.915 # the synch pair is identical
2025-07-02 05:50:51.928 yield '  ' + aelt
2025-07-02 05:50:51.938
2025-07-02 05:50:51.949 # pump out diffs from after the synch point
2025-07-02 05:50:51.961 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:51.971
2025-07-02 05:50:51.983 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:51.992 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:51.999
2025-07-02 05:50:52.006 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:52.014 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:52.020 alo = 342, ahi = 1101
2025-07-02 05:50:52.030 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:52.037 blo = 342, bhi = 1101
2025-07-02 05:50:52.043
2025-07-02 05:50:52.050 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:52.060 g = []
2025-07-02 05:50:52.071 if alo < ahi:
2025-07-02 05:50:52.080 if blo < bhi:
2025-07-02 05:50:52.093 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:52.106 else:
2025-07-02 05:50:52.116 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:52.125 elif blo < bhi:
2025-07-02 05:50:52.139 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:52.148
2025-07-02 05:50:52.156 >       yield from g
2025-07-02 05:50:52.163
2025-07-02 05:50:52.172 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:52.184 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:52.195
2025-07-02 05:50:52.203 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:52.211 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:52.217 alo = 342, ahi = 1101
2025-07-02 05:50:52.227 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:52.240 blo = 342, bhi = 1101
2025-07-02 05:50:52.250
2025-07-02 05:50:52.260 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:52.272 r"""
2025-07-02 05:50:52.284 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:52.300 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:52.310 synch point, and intraline difference marking is done on the
2025-07-02 05:50:52.318 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:52.327
2025-07-02 05:50:52.337 Example:
2025-07-02 05:50:52.347
2025-07-02 05:50:52.355 >>> d = Differ()
2025-07-02 05:50:52.362 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:52.369 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:52.375 >>> print(''.join(results), end="")
2025-07-02 05:50:52.383 - abcDefghiJkl
2025-07-02 05:50:52.407 + abcdefGhijkl
2025-07-02 05:50:52.429 """
2025-07-02 05:50:52.441
2025-07-02 05:50:52.454 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:52.464 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:52.472 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:52.479 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:52.486 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:52.492
2025-07-02 05:50:52.499 # search for the pair that matches best without being identical
2025-07-02 05:50:52.506 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:52.516 # on junk -- unless we have to)
2025-07-02 05:50:52.525 for j in range(blo, bhi):
2025-07-02 05:50:52.535 bj = b[j]
2025-07-02 05:50:52.548 cruncher.set_seq2(bj)
2025-07-02 05:50:52.559 for i in range(alo, ahi):
2025-07-02 05:50:52.567 ai = a[i]
2025-07-02 05:50:52.576 if ai == bj:
2025-07-02 05:50:52.583 if eqi is None:
2025-07-02 05:50:52.589 eqi, eqj = i, j
2025-07-02 05:50:52.594 continue
2025-07-02 05:50:52.605 cruncher.set_seq1(ai)
2025-07-02 05:50:52.614 # computing similarity is expensive, so use the quick
2025-07-02 05:50:52.621 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:52.633 # compares by a factor of 3.
2025-07-02 05:50:52.643 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:52.653 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:52.666 # of the computation is cached by cruncher
2025-07-02 05:50:52.678 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:52.687 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:52.695 cruncher.ratio() > best_ratio:
2025-07-02 05:50:52.702 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:52.709 if best_ratio < cutoff:
2025-07-02 05:50:52.716 # no non-identical "pretty close" pair
2025-07-02 05:50:52.725 if eqi is None:
2025-07-02 05:50:52.732 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:52.739 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:52.745 return
2025-07-02 05:50:52.750 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:52.755 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:52.760 else:
2025-07-02 05:50:52.766 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:52.772 eqi = None
2025-07-02 05:50:52.778
2025-07-02 05:50:52.783 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:52.791 # identical
2025-07-02 05:50:52.797
2025-07-02 05:50:52.804 # pump out diffs from before the synch point
2025-07-02 05:50:52.811 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:52.819
2025-07-02 05:50:52.830 # do intraline marking on the synch pair
2025-07-02 05:50:52.840 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:52.848 if eqi is None:
2025-07-02 05:50:52.854 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:52.860 atags = btags = ""
2025-07-02 05:50:52.867 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:52.880 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:52.890 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:52.901 if tag == 'replace':
2025-07-02 05:50:52.910 atags += '^' * la
2025-07-02 05:50:52.919 btags += '^' * lb
2025-07-02 05:50:52.926 elif tag == 'delete':
2025-07-02 05:50:52.933 atags += '-' * la
2025-07-02 05:50:52.939 elif tag == 'insert':
2025-07-02 05:50:52.946 btags += '+' * lb
2025-07-02 05:50:52.957 elif tag == 'equal':
2025-07-02 05:50:52.970 atags += ' ' * la
2025-07-02 05:50:52.981 btags += ' ' * lb
2025-07-02 05:50:52.992 else:
2025-07-02 05:50:53.005 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:53.017 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:53.024 else:
2025-07-02 05:50:53.032 # the synch pair is identical
2025-07-02 05:50:53.039 yield '  ' + aelt
2025-07-02 05:50:53.047
2025-07-02 05:50:53.060 # pump out diffs from after the synch point
2025-07-02 05:50:53.072 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:53.086
2025-07-02 05:50:53.096 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:53.104 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:53.111
2025-07-02 05:50:53.122 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:53.132 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:53.140 alo = 343, ahi = 1101
2025-07-02 05:50:53.148 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:53.156 blo = 343, bhi = 1101
2025-07-02 05:50:53.163
2025-07-02 05:50:53.169 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:53.174 g = []
2025-07-02 05:50:53.179 if alo < ahi:
2025-07-02 05:50:53.183 if blo < bhi:
2025-07-02 05:50:53.189 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:53.194 else:
2025-07-02 05:50:53.203 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:53.213 elif blo < bhi:
2025-07-02 05:50:53.225 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:53.236
2025-07-02 05:50:53.247 >       yield from g
2025-07-02 05:50:53.260
2025-07-02 05:50:53.272 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:53.283 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:53.295
2025-07-02 05:50:53.306 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:53.317 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:53.324 alo = 343, ahi = 1101
2025-07-02 05:50:53.336 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:53.349 blo = 343, bhi = 1101
2025-07-02 05:50:53.360
2025-07-02 05:50:53.368 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:53.379 r"""
2025-07-02 05:50:53.388 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:53.401 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:53.412 synch point, and intraline difference marking is done on the
2025-07-02 05:50:53.423 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:53.436
2025-07-02 05:50:53.449 Example:
2025-07-02 05:50:53.460
2025-07-02 05:50:53.469 >>> d = Differ()
2025-07-02 05:50:53.477 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:53.487 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:53.500 >>> print(''.join(results), end="")
2025-07-02 05:50:53.511 - abcDefghiJkl
2025-07-02 05:50:53.538 + abcdefGhijkl
2025-07-02 05:50:53.561 """
2025-07-02 05:50:53.572
2025-07-02 05:50:53.581 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:53.588 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:53.596 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:53.602 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:53.612 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:53.624
2025-07-02 05:50:53.632 # search for the pair that matches best without being identical
2025-07-02 05:50:53.640 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:53.647 # on junk -- unless we have to)
2025-07-02 05:50:53.653 for j in range(blo, bhi):
2025-07-02 05:50:53.659 bj = b[j]
2025-07-02 05:50:53.665 cruncher.set_seq2(bj)
2025-07-02 05:50:53.672 for i in range(alo, ahi):
2025-07-02 05:50:53.678 ai = a[i]
2025-07-02 05:50:53.684 if ai == bj:
2025-07-02 05:50:53.690 if eqi is None:
2025-07-02 05:50:53.696 eqi, eqj = i, j
2025-07-02 05:50:53.701 continue
2025-07-02 05:50:53.708 cruncher.set_seq1(ai)
2025-07-02 05:50:53.714 # computing similarity is expensive, so use the quick
2025-07-02 05:50:53.722 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:53.729 # compares by a factor of 3.
2025-07-02 05:50:53.736 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:53.742 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:53.747 # of the computation is cached by cruncher
2025-07-02 05:50:53.755 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:53.765 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:53.774 cruncher.ratio() > best_ratio:
2025-07-02 05:50:53.784 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:53.795 if best_ratio < cutoff:
2025-07-02 05:50:53.804 # no non-identical "pretty close" pair
2025-07-02 05:50:53.812 if eqi is None:
2025-07-02 05:50:53.819 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:53.826 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:53.832 return
2025-07-02 05:50:53.838 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:53.844 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:53.850 else:
2025-07-02 05:50:53.857 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:53.863 eqi = None
2025-07-02 05:50:53.868
2025-07-02 05:50:53.883 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:53.893 # identical
2025-07-02 05:50:53.904
2025-07-02 05:50:53.914 # pump out diffs from before the synch point
2025-07-02 05:50:53.927 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:53.937
2025-07-02 05:50:53.950 # do intraline marking on the synch pair
2025-07-02 05:50:53.960 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:53.969 if eqi is None:
2025-07-02 05:50:53.981 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:53.991 atags = btags = ""
2025-07-02 05:50:53.999 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:54.013 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:54.025 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:54.036 if tag == 'replace':
2025-07-02 05:50:54.044 atags += '^' * la
2025-07-02 05:50:54.052 btags += '^' * lb
2025-07-02 05:50:54.059 elif tag == 'delete':
2025-07-02 05:50:54.067 atags += '-' * la
2025-07-02 05:50:54.075 elif tag == 'insert':
2025-07-02 05:50:54.083 btags += '+' * lb
2025-07-02 05:50:54.092 elif tag == 'equal':
2025-07-02 05:50:54.103 atags += ' ' * la
2025-07-02 05:50:54.114 btags += ' ' * lb
2025-07-02 05:50:54.125 else:
2025-07-02 05:50:54.135 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:54.142 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:54.151 else:
2025-07-02 05:50:54.165 # the synch pair is identical
2025-07-02 05:50:54.174 yield '  ' + aelt
2025-07-02 05:50:54.182
2025-07-02 05:50:54.195 # pump out diffs from after the synch point
2025-07-02 05:50:54.205 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:54.213
2025-07-02 05:50:54.220 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:54.227 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:54.234
2025-07-02 05:50:54.246 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:54.257 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:54.266 alo = 344, ahi = 1101
2025-07-02 05:50:54.274 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:54.281 blo = 344, bhi = 1101
2025-07-02 05:50:54.287
2025-07-02 05:50:54.293 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:54.298 g = []
2025-07-02 05:50:54.307 if alo < ahi:
2025-07-02 05:50:54.317 if blo < bhi:
2025-07-02 05:50:54.323 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:54.330 else:
2025-07-02 05:50:54.336 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:54.346 elif blo < bhi:
2025-07-02 05:50:54.360 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:54.373
2025-07-02 05:50:54.383 >       yield from g
2025-07-02 05:50:54.391
2025-07-02 05:50:54.398 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:54.405 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:54.412
2025-07-02 05:50:54.419 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:54.426 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:54.437 alo = 344, ahi = 1101
2025-07-02 05:50:54.447 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:54.454 blo = 344, bhi = 1101
2025-07-02 05:50:54.467
2025-07-02 05:50:54.477 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:54.484 r"""
2025-07-02 05:50:54.491 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:54.498 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:54.505 synch point, and intraline difference marking is done on the
2025-07-02 05:50:54.518 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:54.529
2025-07-02 05:50:54.539 Example:
2025-07-02 05:50:54.551
2025-07-02 05:50:54.563 >>> d = Differ()
2025-07-02 05:50:54.575 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:54.585 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:54.592 >>> print(''.join(results), end="")
2025-07-02 05:50:54.599 - abcDefghiJkl
2025-07-02 05:50:54.611 + abcdefGhijkl
2025-07-02 05:50:54.624 """
2025-07-02 05:50:54.630
2025-07-02 05:50:54.639 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:54.654 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:54.664 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:54.672 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:54.680 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:54.691
2025-07-02 05:50:54.701 # search for the pair that matches best without being identical
2025-07-02 05:50:54.710 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:54.717 # on junk -- unless we have to)
2025-07-02 05:50:54.722 for j in range(blo, bhi):
2025-07-02 05:50:54.729 bj = b[j]
2025-07-02 05:50:54.736 cruncher.set_seq2(bj)
2025-07-02 05:50:54.746 for i in range(alo, ahi):
2025-07-02 05:50:54.757 ai = a[i]
2025-07-02 05:50:54.765 if ai == bj:
2025-07-02 05:50:54.777 if eqi is None:
2025-07-02 05:50:54.787 eqi, eqj = i, j
2025-07-02 05:50:54.800 continue
2025-07-02 05:50:54.811 cruncher.set_seq1(ai)
2025-07-02 05:50:54.823 # computing similarity is expensive, so use the quick
2025-07-02 05:50:54.832 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:54.840 # compares by a factor of 3.
2025-07-02 05:50:54.847 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:54.854 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:54.863 # of the computation is cached by cruncher
2025-07-02 05:50:54.872 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:54.880 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:54.888 cruncher.ratio() > best_ratio:
2025-07-02 05:50:54.896 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:54.904 if best_ratio < cutoff:
2025-07-02 05:50:54.912 # no non-identical "pretty close" pair
2025-07-02 05:50:54.920 if eqi is None:
2025-07-02 05:50:54.928 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:54.936 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:54.944 return
2025-07-02 05:50:54.962 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:54.971 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:54.977 else:
2025-07-02 05:50:54.983 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:54.989 eqi = None
2025-07-02 05:50:54.994
2025-07-02 05:50:55.000 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:55.006 # identical
2025-07-02 05:50:55.011
2025-07-02 05:50:55.018 # pump out diffs from before the synch point
2025-07-02 05:50:55.026 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:55.033
2025-07-02 05:50:55.040 # do intraline marking on the synch pair
2025-07-02 05:50:55.048 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:55.055 if eqi is None:
2025-07-02 05:50:55.062 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:55.069 atags = btags = ""
2025-07-02 05:50:55.076 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:55.082 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:55.090 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:55.097 if tag == 'replace':
2025-07-02 05:50:55.104 atags += '^' * la
2025-07-02 05:50:55.111 btags += '^' * lb
2025-07-02 05:50:55.118 elif tag == 'delete':
2025-07-02 05:50:55.125 atags += '-' * la
2025-07-02 05:50:55.132 elif tag == 'insert':
2025-07-02 05:50:55.138 btags += '+' * lb
2025-07-02 05:50:55.146 elif tag == 'equal':
2025-07-02 05:50:55.153 atags += ' ' * la
2025-07-02 05:50:55.160 btags += ' ' * lb
2025-07-02 05:50:55.167 else:
2025-07-02 05:50:55.175 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:55.183 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:55.191 else:
2025-07-02 05:50:55.198 # the synch pair is identical
2025-07-02 05:50:55.205 yield '  ' + aelt
2025-07-02 05:50:55.213
2025-07-02 05:50:55.220 # pump out diffs from after the synch point
2025-07-02 05:50:55.228 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:55.236
2025-07-02 05:50:55.243 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:55.251 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:55.259
2025-07-02 05:50:55.267 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:55.277 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:55.290 alo = 345, ahi = 1101
2025-07-02 05:50:55.301 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:55.309 blo = 345, bhi = 1101
2025-07-02 05:50:55.315
2025-07-02 05:50:55.322 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:55.334 g = []
2025-07-02 05:50:55.344 if alo < ahi:
2025-07-02 05:50:55.352 if blo < bhi:
2025-07-02 05:50:55.359 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:55.367 else:
2025-07-02 05:50:55.374 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:55.384 elif blo < bhi:
2025-07-02 05:50:55.391 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:55.397
2025-07-02 05:50:55.402 >       yield from g
2025-07-02 05:50:55.407
2025-07-02 05:50:55.411 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:55.417 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:55.422
2025-07-02 05:50:55.426 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:55.442 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:55.453 alo = 345, ahi = 1101
2025-07-02 05:50:55.463 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:55.470 blo = 345, bhi = 1101
2025-07-02 05:50:55.476
2025-07-02 05:50:55.482 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:55.488 r"""
2025-07-02 05:50:55.494 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:55.500 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:55.506 synch point, and intraline difference marking is done on the
2025-07-02 05:50:55.512 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:55.518
2025-07-02 05:50:55.528 Example:
2025-07-02 05:50:55.536
2025-07-02 05:50:55.543 >>> d = Differ()
2025-07-02 05:50:55.550 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:55.557 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:55.563 >>> print(''.join(results), end="")
2025-07-02 05:50:55.569 - abcDefghiJkl
2025-07-02 05:50:55.581 + abcdefGhijkl
2025-07-02 05:50:55.598 """
2025-07-02 05:50:55.604
2025-07-02 05:50:55.611 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:55.617 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:55.632 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:55.642 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:55.651 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:55.663
2025-07-02 05:50:55.673 # search for the pair that matches best without being identical
2025-07-02 05:50:55.681 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:55.688 # on junk -- unless we have to)
2025-07-02 05:50:55.695 for j in range(blo, bhi):
2025-07-02 05:50:55.703 bj = b[j]
2025-07-02 05:50:55.710 cruncher.set_seq2(bj)
2025-07-02 05:50:55.718 for i in range(alo, ahi):
2025-07-02 05:50:55.726 ai = a[i]
2025-07-02 05:50:55.738 if ai == bj:
2025-07-02 05:50:55.749 if eqi is None:
2025-07-02 05:50:55.762 eqi, eqj = i, j
2025-07-02 05:50:55.772 continue
2025-07-02 05:50:55.782 cruncher.set_seq1(ai)
2025-07-02 05:50:55.794 # computing similarity is expensive, so use the quick
2025-07-02 05:50:55.803 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:55.811 # compares by a factor of 3.
2025-07-02 05:50:55.819 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:55.827 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:55.835 # of the computation is cached by cruncher
2025-07-02 05:50:55.841 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:55.848 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:55.854 cruncher.ratio() > best_ratio:
2025-07-02 05:50:55.863 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:55.875 if best_ratio < cutoff:
2025-07-02 05:50:55.883 # no non-identical "pretty close" pair
2025-07-02 05:50:55.890 if eqi is None:
2025-07-02 05:50:55.899 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:55.913 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:55.925 return
2025-07-02 05:50:55.934 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:55.943 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:55.955 else:
2025-07-02 05:50:55.966 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:55.977 eqi = None
2025-07-02 05:50:55.989
2025-07-02 05:50:56.000 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:56.008 # identical
2025-07-02 05:50:56.014
2025-07-02 05:50:56.020 # pump out diffs from before the synch point
2025-07-02 05:50:56.027 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:56.040
2025-07-02 05:50:56.050 # do intraline marking on the synch pair
2025-07-02 05:50:56.059 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:56.067 if eqi is None:
2025-07-02 05:50:56.076 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:56.083 atags = btags = ""
2025-07-02 05:50:56.089 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:56.100 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:56.111 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:56.118 if tag == 'replace':
2025-07-02 05:50:56.124 atags += '^' * la
2025-07-02 05:50:56.131 btags += '^' * lb
2025-07-02 05:50:56.139 elif tag == 'delete':
2025-07-02 05:50:56.147 atags += '-' * la
2025-07-02 05:50:56.157 elif tag == 'insert':
2025-07-02 05:50:56.166 btags += '+' * lb
2025-07-02 05:50:56.172 elif tag == 'equal':
2025-07-02 05:50:56.177 atags += ' ' * la
2025-07-02 05:50:56.182 btags += ' ' * lb
2025-07-02 05:50:56.187 else:
2025-07-02 05:50:56.196 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:56.205 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:56.214 else:
2025-07-02 05:50:56.222 # the synch pair is identical
2025-07-02 05:50:56.231 yield '  ' + aelt
2025-07-02 05:50:56.238
2025-07-02 05:50:56.249 # pump out diffs from after the synch point
2025-07-02 05:50:56.258 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:56.265
2025-07-02 05:50:56.277 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:56.287 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:56.293
2025-07-02 05:50:56.298 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:56.304 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:56.308 alo = 346, ahi = 1101
2025-07-02 05:50:56.313 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:56.318 blo = 346, bhi = 1101
2025-07-02 05:50:56.322
2025-07-02 05:50:56.327 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:56.331 g = []
2025-07-02 05:50:56.336 if alo < ahi:
2025-07-02 05:50:56.340 if blo < bhi:
2025-07-02 05:50:56.346 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:56.351 else:
2025-07-02 05:50:56.356 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:56.360 elif blo < bhi:
2025-07-02 05:50:56.365 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:56.378
2025-07-02 05:50:56.388 >       yield from g
2025-07-02 05:50:56.397
2025-07-02 05:50:56.406 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:56.414 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:56.421
2025-07-02 05:50:56.427 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:56.436 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:56.444 alo = 346, ahi = 1101
2025-07-02 05:50:56.452 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:56.459 blo = 346, bhi = 1101
2025-07-02 05:50:56.466
2025-07-02 05:50:56.475 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:56.487 r"""
2025-07-02 05:50:56.495 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:56.503 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:56.508 synch point, and intraline difference marking is done on the
2025-07-02 05:50:56.514 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:56.519
2025-07-02 05:50:56.525 Example:
2025-07-02 05:50:56.530
2025-07-02 05:50:56.536 >>> d = Differ()
2025-07-02 05:50:56.543 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:56.551 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:56.563 >>> print(''.join(results), end="")
2025-07-02 05:50:56.571 - abcDefghiJkl
2025-07-02 05:50:56.591 + abcdefGhijkl
2025-07-02 05:50:56.609 """
2025-07-02 05:50:56.617
2025-07-02 05:50:56.623 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:56.630 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:56.636 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:56.648 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:56.661 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:56.671
2025-07-02 05:50:56.679 # search for the pair that matches best without being identical
2025-07-02 05:50:56.685 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:56.691 # on junk -- unless we have to)
2025-07-02 05:50:56.696 for j in range(blo, bhi):
2025-07-02 05:50:56.701 bj = b[j]
2025-07-02 05:50:56.706 cruncher.set_seq2(bj)
2025-07-02 05:50:56.711 for i in range(alo, ahi):
2025-07-02 05:50:56.723 ai = a[i]
2025-07-02 05:50:56.733 if ai == bj:
2025-07-02 05:50:56.741 if eqi is None:
2025-07-02 05:50:56.748 eqi, eqj = i, j
2025-07-02 05:50:56.755 continue
2025-07-02 05:50:56.762 cruncher.set_seq1(ai)
2025-07-02 05:50:56.771 # computing similarity is expensive, so use the quick
2025-07-02 05:50:56.782 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:56.795 # compares by a factor of 3.
2025-07-02 05:50:56.805 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:56.813 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:56.828 # of the computation is cached by cruncher
2025-07-02 05:50:56.838 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:56.851 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:56.863 cruncher.ratio() > best_ratio:
2025-07-02 05:50:56.875 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:56.885 if best_ratio < cutoff:
2025-07-02 05:50:56.897 # no non-identical "pretty close" pair
2025-07-02 05:50:56.907 if eqi is None:
2025-07-02 05:50:56.916 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:56.924 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:56.931 return
2025-07-02 05:50:56.937 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:56.943 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:56.950 else:
2025-07-02 05:50:56.956 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:56.963 eqi = None
2025-07-02 05:50:56.970
2025-07-02 05:50:56.977 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:56.984 # identical
2025-07-02 05:50:56.991
2025-07-02 05:50:56.999 # pump out diffs from before the synch point
2025-07-02 05:50:57.012 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:57.023
2025-07-02 05:50:57.030 # do intraline marking on the synch pair
2025-07-02 05:50:57.036 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:57.045 if eqi is None:
2025-07-02 05:50:57.055 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:57.066 atags = btags = ""
2025-07-02 05:50:57.078 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:57.087 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:57.095 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:57.101 if tag == 'replace':
2025-07-02 05:50:57.111 atags += '^' * la
2025-07-02 05:50:57.121 btags += '^' * lb
2025-07-02 05:50:57.129 elif tag == 'delete':
2025-07-02 05:50:57.136 atags += '-' * la
2025-07-02 05:50:57.142 elif tag == 'insert':
2025-07-02 05:50:57.148 btags += '+' * lb
2025-07-02 05:50:57.154 elif tag == 'equal':
2025-07-02 05:50:57.161 atags += ' ' * la
2025-07-02 05:50:57.168 btags += ' ' * lb
2025-07-02 05:50:57.174 else:
2025-07-02 05:50:57.181 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:57.188 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:57.194 else:
2025-07-02 05:50:57.200 # the synch pair is identical
2025-07-02 05:50:57.206 yield '  ' + aelt
2025-07-02 05:50:57.211
2025-07-02 05:50:57.220 # pump out diffs from after the synch point
2025-07-02 05:50:57.231 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:57.239
2025-07-02 05:50:57.247 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:57.254 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:57.266
2025-07-02 05:50:57.276 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:57.292 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:57.304 alo = 347, ahi = 1101
2025-07-02 05:50:57.316 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:57.323 blo = 347, bhi = 1101
2025-07-02 05:50:57.332
2025-07-02 05:50:57.341 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:57.348 g = []
2025-07-02 05:50:57.356 if alo < ahi:
2025-07-02 05:50:57.362 if blo < bhi:
2025-07-02 05:50:57.375 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:57.385 else:
2025-07-02 05:50:57.396 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:57.408 elif blo < bhi:
2025-07-02 05:50:57.416 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:57.428
2025-07-02 05:50:57.443 >       yield from g
2025-07-02 05:50:57.450
2025-07-02 05:50:57.457 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:57.463 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:57.469
2025-07-02 05:50:57.480 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:57.491 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:57.504 alo = 347, ahi = 1101
2025-07-02 05:50:57.518 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:57.530 blo = 347, bhi = 1101
2025-07-02 05:50:57.537
2025-07-02 05:50:57.542 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:57.547 r"""
2025-07-02 05:50:57.554 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:57.559 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:57.564 synch point, and intraline difference marking is done on the
2025-07-02 05:50:57.568 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:57.573
2025-07-02 05:50:57.577 Example:
2025-07-02 05:50:57.585
2025-07-02 05:50:57.597 >>> d = Differ()
2025-07-02 05:50:57.606 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:57.615 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:57.622 >>> print(''.join(results), end="")
2025-07-02 05:50:57.628 - abcDefghiJkl
2025-07-02 05:50:57.645 + abcdefGhijkl
2025-07-02 05:50:57.660 """
2025-07-02 05:50:57.673
2025-07-02 05:50:57.684 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:57.696 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:57.708 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:57.720 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:57.731 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:57.746
2025-07-02 05:50:57.754 # search for the pair that matches best without being identical
2025-07-02 05:50:57.761 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:57.766 # on junk -- unless we have to)
2025-07-02 05:50:57.770 for j in range(blo, bhi):
2025-07-02 05:50:57.775 bj = b[j]
2025-07-02 05:50:57.780 cruncher.set_seq2(bj)
2025-07-02 05:50:57.784 for i in range(alo, ahi):
2025-07-02 05:50:57.789 ai = a[i]
2025-07-02 05:50:57.793 if ai == bj:
2025-07-02 05:50:57.798 if eqi is None:
2025-07-02 05:50:57.803 eqi, eqj = i, j
2025-07-02 05:50:57.811 continue
2025-07-02 05:50:57.822 cruncher.set_seq1(ai)
2025-07-02 05:50:57.828 # computing similarity is expensive, so use the quick
2025-07-02 05:50:57.834 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:57.840 # compares by a factor of 3.
2025-07-02 05:50:57.845 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:57.850 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:57.855 # of the computation is cached by cruncher
2025-07-02 05:50:57.859 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:57.864 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:57.868 cruncher.ratio() > best_ratio:
2025-07-02 05:50:57.873 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:57.878 if best_ratio < cutoff:
2025-07-02 05:50:57.884 # no non-identical "pretty close" pair
2025-07-02 05:50:57.890 if eqi is None:
2025-07-02 05:50:57.898 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:57.905 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:57.911 return
2025-07-02 05:50:57.916 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:57.920 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:57.926 else:
2025-07-02 05:50:57.934 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:57.940 eqi = None
2025-07-02 05:50:57.946
2025-07-02 05:50:57.958 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:57.967 # identical
2025-07-02 05:50:57.976
2025-07-02 05:50:57.984 # pump out diffs from before the synch point
2025-07-02 05:50:57.991 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:57.997
2025-07-02 05:50:58.003 # do intraline marking on the synch pair
2025-07-02 05:50:58.010 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:58.016 if eqi is None:
2025-07-02 05:50:58.022 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:58.027 atags = btags = ""
2025-07-02 05:50:58.032 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:58.037 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:58.042 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:58.047 if tag == 'replace':
2025-07-02 05:50:58.053 atags += '^' * la
2025-07-02 05:50:58.060 btags += '^' * lb
2025-07-02 05:50:58.067 elif tag == 'delete':
2025-07-02 05:50:58.074 atags += '-' * la
2025-07-02 05:50:58.082 elif tag == 'insert':
2025-07-02 05:50:58.089 btags += '+' * lb
2025-07-02 05:50:58.095 elif tag == 'equal':
2025-07-02 05:50:58.100 atags += ' ' * la
2025-07-02 05:50:58.105 btags += ' ' * lb
2025-07-02 05:50:58.110 else:
2025-07-02 05:50:58.114 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:58.119 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:58.124 else:
2025-07-02 05:50:58.131 # the synch pair is identical
2025-07-02 05:50:58.139 yield '  ' + aelt
2025-07-02 05:50:58.150
2025-07-02 05:50:58.159 # pump out diffs from after the synch point
2025-07-02 05:50:58.167 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:58.173
2025-07-02 05:50:58.182 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:58.195 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:58.205
2025-07-02 05:50:58.214 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:58.223 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:58.233 alo = 348, ahi = 1101
2025-07-02 05:50:58.246 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:58.254 blo = 348, bhi = 1101
2025-07-02 05:50:58.261
2025-07-02 05:50:58.268 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:58.275 g = []
2025-07-02 05:50:58.287 if alo < ahi:
2025-07-02 05:50:58.299 if blo < bhi:
2025-07-02 05:50:58.307 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:58.315 else:
2025-07-02 05:50:58.322 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:58.329 elif blo < bhi:
2025-07-02 05:50:58.342 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:58.351
2025-07-02 05:50:58.358 >       yield from g
2025-07-02 05:50:58.370
2025-07-02 05:50:58.383 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:58.393 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:58.401
2025-07-02 05:50:58.407 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:58.418 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:58.426 alo = 348, ahi = 1101
2025-07-02 05:50:58.434 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:58.439 blo = 348, bhi = 1101
2025-07-02 05:50:58.445
2025-07-02 05:50:58.456 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:58.466 r"""
2025-07-02 05:50:58.473 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:58.480 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:58.486 synch point, and intraline difference marking is done on the
2025-07-02 05:50:58.497 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:58.510
2025-07-02 05:50:58.520 Example:
2025-07-02 05:50:58.532
2025-07-02 05:50:58.543 >>> d = Differ()
2025-07-02 05:50:58.555 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:58.568 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:58.582 >>> print(''.join(results), end="")
2025-07-02 05:50:58.593 - abcDefghiJkl
2025-07-02 05:50:58.616 + abcdefGhijkl
2025-07-02 05:50:58.640 """
2025-07-02 05:50:58.652
2025-07-02 05:50:58.663 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:58.671 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:58.683 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:58.697 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:58.711 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:58.726
2025-07-02 05:50:58.739 # search for the pair that matches best without being identical
2025-07-02 05:50:58.750 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:58.762 # on junk -- unless we have to)
2025-07-02 05:50:58.773 for j in range(blo, bhi):
2025-07-02 05:50:58.786 bj = b[j]
2025-07-02 05:50:58.796 cruncher.set_seq2(bj)
2025-07-02 05:50:58.804 for i in range(alo, ahi):
2025-07-02 05:50:58.816 ai = a[i]
2025-07-02 05:50:58.830 if ai == bj:
2025-07-02 05:50:58.843 if eqi is None:
2025-07-02 05:50:58.853 eqi, eqj = i, j
2025-07-02 05:50:58.860 continue
2025-07-02 05:50:58.867 cruncher.set_seq1(ai)
2025-07-02 05:50:58.873 # computing similarity is expensive, so use the quick
2025-07-02 05:50:58.879 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:58.886 # compares by a factor of 3.
2025-07-02 05:50:58.893 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:58.899 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:58.908 # of the computation is cached by cruncher
2025-07-02 05:50:58.922 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:58.930 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:58.940 cruncher.ratio() > best_ratio:
2025-07-02 05:50:58.952 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:58.965 if best_ratio < cutoff:
2025-07-02 05:50:58.977 # no non-identical "pretty close" pair
2025-07-02 05:50:58.988 if eqi is None:
2025-07-02 05:50:58.995 # no identical pair either -- treat it as a straight replace
2025-07-02 05:50:59.001 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:59.006 return
2025-07-02 05:50:59.011 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:50:59.016 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:50:59.021 else:
2025-07-02 05:50:59.026 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:50:59.030 eqi = None
2025-07-02 05:50:59.035
2025-07-02 05:50:59.040 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:50:59.045 # identical
2025-07-02 05:50:59.050
2025-07-02 05:50:59.060 # pump out diffs from before the synch point
2025-07-02 05:50:59.074 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:50:59.087
2025-07-02 05:50:59.095 # do intraline marking on the synch pair
2025-07-02 05:50:59.102 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:50:59.109 if eqi is None:
2025-07-02 05:50:59.114 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:50:59.119 atags = btags = ""
2025-07-02 05:50:59.124 cruncher.set_seqs(aelt, belt)
2025-07-02 05:50:59.131 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:50:59.139 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:50:59.147 if tag == 'replace':
2025-07-02 05:50:59.154 atags += '^' * la
2025-07-02 05:50:59.164 btags += '^' * lb
2025-07-02 05:50:59.173 elif tag == 'delete':
2025-07-02 05:50:59.179 atags += '-' * la
2025-07-02 05:50:59.184 elif tag == 'insert':
2025-07-02 05:50:59.192 btags += '+' * lb
2025-07-02 05:50:59.198 elif tag == 'equal':
2025-07-02 05:50:59.203 atags += ' ' * la
2025-07-02 05:50:59.208 btags += ' ' * lb
2025-07-02 05:50:59.213 else:
2025-07-02 05:50:59.217 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:50:59.222 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:50:59.227 else:
2025-07-02 05:50:59.233 # the synch pair is identical
2025-07-02 05:50:59.246 yield '  ' + aelt
2025-07-02 05:50:59.259
2025-07-02 05:50:59.271 # pump out diffs from after the synch point
2025-07-02 05:50:59.282 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:50:59.294
2025-07-02 05:50:59.307 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:50:59.315 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:59.322
2025-07-02 05:50:59.330 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:59.339 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:59.349 alo = 349, ahi = 1101
2025-07-02 05:50:59.356 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:59.364 blo = 349, bhi = 1101
2025-07-02 05:50:59.370
2025-07-02 05:50:59.377 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:59.383 g = []
2025-07-02 05:50:59.389 if alo < ahi:
2025-07-02 05:50:59.395 if blo < bhi:
2025-07-02 05:50:59.402 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:50:59.408 else:
2025-07-02 05:50:59.414 g = self._dump('-', a, alo, ahi)
2025-07-02 05:50:59.424 elif blo < bhi:
2025-07-02 05:50:59.437 g = self._dump('+', b, blo, bhi)
2025-07-02 05:50:59.446
2025-07-02 05:50:59.455 >       yield from g
2025-07-02 05:50:59.462
2025-07-02 05:50:59.472 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:50:59.484 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:50:59.494
2025-07-02 05:50:59.503 self = <difflib.Differ object at [hex]>
2025-07-02 05:50:59.510 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:50:59.523 alo = 349, ahi = 1101
2025-07-02 05:50:59.534 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:50:59.543 blo = 349, bhi = 1101
2025-07-02 05:50:59.549
2025-07-02 05:50:59.555 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:50:59.562 r"""
2025-07-02 05:50:59.570 When replacing one block of lines with another, search the blocks
2025-07-02 05:50:59.577 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:50:59.586 synch point, and intraline difference marking is done on the
2025-07-02 05:50:59.594 similar pair. Lots of work, but often worth it.
2025-07-02 05:50:59.603
2025-07-02 05:50:59.614 Example:
2025-07-02 05:50:59.627
2025-07-02 05:50:59.640 >>> d = Differ()
2025-07-02 05:50:59.653 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:50:59.664 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:50:59.675 >>> print(''.join(results), end="")
2025-07-02 05:50:59.684 - abcDefghiJkl
2025-07-02 05:50:59.706 + abcdefGhijkl
2025-07-02 05:50:59.729 """
2025-07-02 05:50:59.738
2025-07-02 05:50:59.750 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:50:59.762 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:50:59.772 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:50:59.779 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:50:59.785 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:50:59.791
2025-07-02 05:50:59.797 # search for the pair that matches best without being identical
2025-07-02 05:50:59.807 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:50:59.817 # on junk -- unless we have to)
2025-07-02 05:50:59.826 for j in range(blo, bhi):
2025-07-02 05:50:59.835 bj = b[j]
2025-07-02 05:50:59.845 cruncher.set_seq2(bj)
2025-07-02 05:50:59.855 for i in range(alo, ahi):
2025-07-02 05:50:59.864 ai = a[i]
2025-07-02 05:50:59.871 if ai == bj:
2025-07-02 05:50:59.877 if eqi is None:
2025-07-02 05:50:59.884 eqi, eqj = i, j
2025-07-02 05:50:59.890 continue
2025-07-02 05:50:59.897 cruncher.set_seq1(ai)
2025-07-02 05:50:59.903 # computing similarity is expensive, so use the quick
2025-07-02 05:50:59.910 # upper bounds first -- have seen this speed up messy
2025-07-02 05:50:59.923 # compares by a factor of 3.
2025-07-02 05:50:59.934 # note that ratio() is only expensive to compute the first
2025-07-02 05:50:59.941 # time it's called on a sequence pair; the expensive part
2025-07-02 05:50:59.947 # of the computation is cached by cruncher
2025-07-02 05:50:59.960 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:50:59.971 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:50:59.978 cruncher.ratio() > best_ratio:
2025-07-02 05:50:59.983 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:50:59.988 if best_ratio < cutoff:
2025-07-02 05:50:59.993 # no non-identical "pretty close" pair
2025-07-02 05:51:00.005 if eqi is None:
2025-07-02 05:51:00.015 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:00.025 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:00.033 return
2025-07-02 05:51:00.040 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:00.047 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:00.054 else:
2025-07-02 05:51:00.061 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:00.068 eqi = None
2025-07-02 05:51:00.077
2025-07-02 05:51:00.089 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:00.098 # identical
2025-07-02 05:51:00.111
2025-07-02 05:51:00.120 # pump out diffs from before the synch point
2025-07-02 05:51:00.127 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:00.140
2025-07-02 05:51:00.149 # do intraline marking on the synch pair
2025-07-02 05:51:00.157 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:00.164 if eqi is None:
2025-07-02 05:51:00.170 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:00.177 atags = btags = ""
2025-07-02 05:51:00.187 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:00.194 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:00.203 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:00.210 if tag == 'replace':
2025-07-02 05:51:00.217 atags += '^' * la
2025-07-02 05:51:00.223 btags += '^' * lb
2025-07-02 05:51:00.229 elif tag == 'delete':
2025-07-02 05:51:00.235 atags += '-' * la
2025-07-02 05:51:00.243 elif tag == 'insert':
2025-07-02 05:51:00.253 btags += '+' * lb
2025-07-02 05:51:00.261 elif tag == 'equal':
2025-07-02 05:51:00.269 atags += ' ' * la
2025-07-02 05:51:00.275 btags += ' ' * lb
2025-07-02 05:51:00.282 else:
2025-07-02 05:51:00.289 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:00.301 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:00.314 else:
2025-07-02 05:51:00.326 # the synch pair is identical
2025-07-02 05:51:00.335 yield '  ' + aelt
2025-07-02 05:51:00.343
2025-07-02 05:51:00.351 # pump out diffs from after the synch point
2025-07-02 05:51:00.365 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:00.379
2025-07-02 05:51:00.390 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:00.399 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:00.412
2025-07-02 05:51:00.424 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:00.437 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:00.444 alo = 350, ahi = 1101
2025-07-02 05:51:00.457 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:00.467 blo = 350, bhi = 1101
2025-07-02 05:51:00.474
2025-07-02 05:51:00.481 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:00.488 g = []
2025-07-02 05:51:00.500 if alo < ahi:
2025-07-02 05:51:00.511 if blo < bhi:
2025-07-02 05:51:00.520 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:00.528 else:
2025-07-02 05:51:00.536 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:00.543 elif blo < bhi:
2025-07-02 05:51:00.550 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:00.562
2025-07-02 05:51:00.571 >       yield from g
2025-07-02 05:51:00.578
2025-07-02 05:51:00.586 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:00.594 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:00.602
2025-07-02 05:51:00.614 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:00.625 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:00.637 alo = 350, ahi = 1101
2025-07-02 05:51:00.650 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:00.661 blo = 350, bhi = 1101
2025-07-02 05:51:00.674
2025-07-02 05:51:00.685 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:00.696 r"""
2025-07-02 05:51:00.709 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:00.719 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:00.729 synch point, and intraline difference marking is done on the
2025-07-02 05:51:00.737 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:00.743
2025-07-02 05:51:00.752 Example:
2025-07-02 05:51:00.766
2025-07-02 05:51:00.775 >>> d = Differ()
2025-07-02 05:51:00.781 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:00.788 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:00.795 >>> print(''.join(results), end="")
2025-07-02 05:51:00.808 - abcDefghiJkl
2025-07-02 05:51:00.826 + abcdefGhijkl
2025-07-02 05:51:00.847 """
2025-07-02 05:51:00.855
2025-07-02 05:51:00.863 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:00.869 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:00.876 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:00.882 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:00.888 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:00.895
2025-07-02 05:51:00.901 # search for the pair that matches best without being identical
2025-07-02 05:51:00.908 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:00.915 # on junk -- unless we have to)
2025-07-02 05:51:00.921 for j in range(blo, bhi):
2025-07-02 05:51:00.929 bj = b[j]
2025-07-02 05:51:00.941 cruncher.set_seq2(bj)
2025-07-02 05:51:00.953 for i in range(alo, ahi):
2025-07-02 05:51:00.961 ai = a[i]
2025-07-02 05:51:00.968 if ai == bj:
2025-07-02 05:51:00.975 if eqi is None:
2025-07-02 05:51:00.982 eqi, eqj = i, j
2025-07-02 05:51:00.988 continue
2025-07-02 05:51:00.993 cruncher.set_seq1(ai)
2025-07-02 05:51:01.005 # computing similarity is expensive, so use the quick
2025-07-02 05:51:01.014 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:01.024 # compares by a factor of 3.
2025-07-02 05:51:01.034 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:01.046 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:01.056 # of the computation is cached by cruncher
2025-07-02 05:51:01.064 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:01.075 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:01.087 cruncher.ratio() > best_ratio:
2025-07-02 05:51:01.097 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:01.109 if best_ratio < cutoff:
2025-07-02 05:51:01.121 # no non-identical "pretty close" pair
2025-07-02 05:51:01.135 if eqi is None:
2025-07-02 05:51:01.146 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:01.158 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:01.170 return
2025-07-02 05:51:01.181 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:01.190 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:01.201 else:
2025-07-02 05:51:01.214 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:01.225 eqi = None
2025-07-02 05:51:01.237
2025-07-02 05:51:01.248 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:01.257 # identical
2025-07-02 05:51:01.268
2025-07-02 05:51:01.282 # pump out diffs from before the synch point
2025-07-02 05:51:01.290 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:01.299
2025-07-02 05:51:01.312 # do intraline marking on the synch pair
2025-07-02 05:51:01.322 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:01.329 if eqi is None:
2025-07-02 05:51:01.337 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:01.345 atags = btags = ""
2025-07-02 05:51:01.355 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:01.365 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:01.373 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:01.381 if tag == 'replace':
2025-07-02 05:51:01.387 atags += '^' * la
2025-07-02 05:51:01.394 btags += '^' * lb
2025-07-02 05:51:01.399 elif tag == 'delete':
2025-07-02 05:51:01.405 atags += '-' * la
2025-07-02 05:51:01.410 elif tag == 'insert':
2025-07-02 05:51:01.415 btags += '+' * lb
2025-07-02 05:51:01.422 elif tag == 'equal':
2025-07-02 05:51:01.429 atags += ' ' * la
2025-07-02 05:51:01.435 btags += ' ' * lb
2025-07-02 05:51:01.442 else:
2025-07-02 05:51:01.453 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:01.465 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:01.478 else:
2025-07-02 05:51:01.489 # the synch pair is identical
2025-07-02 05:51:01.497 yield '  ' + aelt
2025-07-02 05:51:01.503
2025-07-02 05:51:01.510 # pump out diffs from after the synch point
2025-07-02 05:51:01.518 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:01.529
2025-07-02 05:51:01.538 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:01.549 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:01.555
2025-07-02 05:51:01.562 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:01.569 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:01.575 alo = 351, ahi = 1101
2025-07-02 05:51:01.586 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:01.594 blo = 351, bhi = 1101
2025-07-02 05:51:01.600
2025-07-02 05:51:01.607 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:01.614 g = []
2025-07-02 05:51:01.628 if alo < ahi:
2025-07-02 05:51:01.635 if blo < bhi:
2025-07-02 05:51:01.642 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:01.648 else:
2025-07-02 05:51:01.653 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:01.661 elif blo < bhi:
2025-07-02 05:51:01.668 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:01.674
2025-07-02 05:51:01.679 >       yield from g
2025-07-02 05:51:01.686
2025-07-02 05:51:01.693 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:01.703 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:01.716
2025-07-02 05:51:01.726 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:01.737 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:01.745 alo = 351, ahi = 1101
2025-07-02 05:51:01.753 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:01.760 blo = 351, bhi = 1101
2025-07-02 05:51:01.766
2025-07-02 05:51:01.772 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:01.778 r"""
2025-07-02 05:51:01.784 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:01.791 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:01.798 synch point, and intraline difference marking is done on the
2025-07-02 05:51:01.805 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:01.812
2025-07-02 05:51:01.820 Example:
2025-07-02 05:51:01.828
2025-07-02 05:51:01.836 >>> d = Differ()
2025-07-02 05:51:01.843 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:01.850 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:01.858 >>> print(''.join(results), end="")
2025-07-02 05:51:01.866 - abcDefghiJkl
2025-07-02 05:51:01.879 + abcdefGhijkl
2025-07-02 05:51:01.899 """
2025-07-02 05:51:01.910
2025-07-02 05:51:01.916 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:01.922 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:01.928 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:01.934 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:01.941 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:01.953
2025-07-02 05:51:01.961 # search for the pair that matches best without being identical
2025-07-02 05:51:01.969 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:01.977 # on junk -- unless we have to)
2025-07-02 05:51:01.985 for j in range(blo, bhi):
2025-07-02 05:51:01.992 bj = b[j]
2025-07-02 05:51:01.999 cruncher.set_seq2(bj)
2025-07-02 05:51:02.007 for i in range(alo, ahi):
2025-07-02 05:51:02.014 ai = a[i]
2025-07-02 05:51:02.022 if ai == bj:
2025-07-02 05:51:02.029 if eqi is None:
2025-07-02 05:51:02.037 eqi, eqj = i, j
2025-07-02 05:51:02.044 continue
2025-07-02 05:51:02.051 cruncher.set_seq1(ai)
2025-07-02 05:51:02.058 # computing similarity is expensive, so use the quick
2025-07-02 05:51:02.065 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:02.073 # compares by a factor of 3.
2025-07-02 05:51:02.085 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:02.097 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:02.110 # of the computation is cached by cruncher
2025-07-02 05:51:02.118 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:02.128 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:02.136 cruncher.ratio() > best_ratio:
2025-07-02 05:51:02.144 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:02.151 if best_ratio < cutoff:
2025-07-02 05:51:02.160 # no non-identical "pretty close" pair
2025-07-02 05:51:02.172 if eqi is None:
2025-07-02 05:51:02.182 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:02.193 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:02.207 return
2025-07-02 05:51:02.222 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:02.232 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:02.239 else:
2025-07-02 05:51:02.245 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:02.251 eqi = None
2025-07-02 05:51:02.263
2025-07-02 05:51:02.273 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:02.287 # identical
2025-07-02 05:51:02.296
2025-07-02 05:51:02.303 # pump out diffs from before the synch point
2025-07-02 05:51:02.310 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:02.315
2025-07-02 05:51:02.322 # do intraline marking on the synch pair
2025-07-02 05:51:02.328 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:02.333 if eqi is None:
2025-07-02 05:51:02.340 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:02.348 atags = btags = ""
2025-07-02 05:51:02.354 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:02.361 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:02.368 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:02.376 if tag == 'replace':
2025-07-02 05:51:02.390 atags += '^' * la
2025-07-02 05:51:02.402 btags += '^' * lb
2025-07-02 05:51:02.411 elif tag == 'delete':
2025-07-02 05:51:02.420 atags += '-' * la
2025-07-02 05:51:02.430 elif tag == 'insert':
2025-07-02 05:51:02.441 btags += '+' * lb
2025-07-02 05:51:02.452 elif tag == 'equal':
2025-07-02 05:51:02.461 atags += ' ' * la
2025-07-02 05:51:02.470 btags += ' ' * lb
2025-07-02 05:51:02.477 else:
2025-07-02 05:51:02.483 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:02.490 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:02.501 else:
2025-07-02 05:51:02.509 # the synch pair is identical
2025-07-02 05:51:02.520 yield '  ' + aelt
2025-07-02 05:51:02.532
2025-07-02 05:51:02.540 # pump out diffs from after the synch point
2025-07-02 05:51:02.546 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:02.553
2025-07-02 05:51:02.559 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:02.565 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:02.572
2025-07-02 05:51:02.580 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:02.591 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:02.598 alo = 352, ahi = 1101
2025-07-02 05:51:02.607 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:02.613 blo = 352, bhi = 1101
2025-07-02 05:51:02.619
2025-07-02 05:51:02.626 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:02.635 g = []
2025-07-02 05:51:02.646 if alo < ahi:
2025-07-02 05:51:02.655 if blo < bhi:
2025-07-02 05:51:02.663 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:02.669 else:
2025-07-02 05:51:02.682 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:02.693 elif blo < bhi:
2025-07-02 05:51:02.704 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:02.715
2025-07-02 05:51:02.728 >       yield from g
2025-07-02 05:51:02.737
2025-07-02 05:51:02.751 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:02.760 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:02.767
2025-07-02 05:51:02.774 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:02.782 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:02.791 alo = 352, ahi = 1101
2025-07-02 05:51:02.802 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:02.810 blo = 352, bhi = 1101
2025-07-02 05:51:02.817
2025-07-02 05:51:02.828 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:02.838 r"""
2025-07-02 05:51:02.845 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:02.851 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:02.856 synch point, and intraline difference marking is done on the
2025-07-02 05:51:02.861 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:02.865
2025-07-02 05:51:02.871 Example:
2025-07-02 05:51:02.880
2025-07-02 05:51:02.889 >>> d = Differ()
2025-07-02 05:51:02.895 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:02.901 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:02.907 >>> print(''.join(results), end="")
2025-07-02 05:51:02.913 - abcDefghiJkl
2025-07-02 05:51:02.925 + abcdefGhijkl
2025-07-02 05:51:02.938 """
2025-07-02 05:51:02.946
2025-07-02 05:51:02.953 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:02.960 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:02.965 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:02.970 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:02.975 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:02.979
2025-07-02 05:51:02.984 # search for the pair that matches best without being identical
2025-07-02 05:51:02.989 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:02.994 # on junk -- unless we have to)
2025-07-02 05:51:02.999 for j in range(blo, bhi):
2025-07-02 05:51:03.005 bj = b[j]
2025-07-02 05:51:03.013 cruncher.set_seq2(bj)
2025-07-02 05:51:03.020 for i in range(alo, ahi):
2025-07-02 05:51:03.026 ai = a[i]
2025-07-02 05:51:03.035 if ai == bj:
2025-07-02 05:51:03.044 if eqi is None:
2025-07-02 05:51:03.055 eqi, eqj = i, j
2025-07-02 05:51:03.065 continue
2025-07-02 05:51:03.076 cruncher.set_seq1(ai)
2025-07-02 05:51:03.083 # computing similarity is expensive, so use the quick
2025-07-02 05:51:03.091 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:03.101 # compares by a factor of 3.
2025-07-02 05:51:03.108 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:03.115 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:03.121 # of the computation is cached by cruncher
2025-07-02 05:51:03.128 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:03.134 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:03.143 cruncher.ratio() > best_ratio:
2025-07-02 05:51:03.151 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:03.164 if best_ratio < cutoff:
2025-07-02 05:51:03.173 # no non-identical "pretty close" pair
2025-07-02 05:51:03.180 if eqi is None:
2025-07-02 05:51:03.188 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:03.197 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:03.205 return
2025-07-02 05:51:03.220 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:03.230 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:03.238 else:
2025-07-02 05:51:03.245 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:03.252 eqi = None
2025-07-02 05:51:03.260
2025-07-02 05:51:03.268 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:03.275 # identical
2025-07-02 05:51:03.283
2025-07-02 05:51:03.291 # pump out diffs from before the synch point
2025-07-02 05:51:03.298 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:03.307
2025-07-02 05:51:03.318 # do intraline marking on the synch pair
2025-07-02 05:51:03.329 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:03.337 if eqi is None:
2025-07-02 05:51:03.346 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:03.353 atags = btags = ""
2025-07-02 05:51:03.361 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:03.369 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:03.377 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:03.384 if tag == 'replace':
2025-07-02 05:51:03.390 atags += '^' * la
2025-07-02 05:51:03.396 btags += '^' * lb
2025-07-02 05:51:03.401 elif tag == 'delete':
2025-07-02 05:51:03.408 atags += '-' * la
2025-07-02 05:51:03.414 elif tag == 'insert':
2025-07-02 05:51:03.421 btags += '+' * lb
2025-07-02 05:51:03.427 elif tag == 'equal':
2025-07-02 05:51:03.435 atags += ' ' * la
2025-07-02 05:51:03.442 btags += ' ' * lb
2025-07-02 05:51:03.451 else:
2025-07-02 05:51:03.463 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:03.471 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:03.477 else:
2025-07-02 05:51:03.487 # the synch pair is identical
2025-07-02 05:51:03.495 yield '  ' + aelt
2025-07-02 05:51:03.501
2025-07-02 05:51:03.509 # pump out diffs from after the synch point
2025-07-02 05:51:03.520 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:03.532
2025-07-02 05:51:03.540 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:03.546 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:03.552
2025-07-02 05:51:03.557 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:03.565 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:03.571 alo = 353, ahi = 1101
2025-07-02 05:51:03.578 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:03.586 blo = 353, bhi = 1101
2025-07-02 05:51:03.592
2025-07-02 05:51:03.599 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:03.606 g = []
2025-07-02 05:51:03.612 if alo < ahi:
2025-07-02 05:51:03.617 if blo < bhi:
2025-07-02 05:51:03.623 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:03.629 else:
2025-07-02 05:51:03.635 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:03.642 elif blo < bhi:
2025-07-02 05:51:03.652 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:03.660
2025-07-02 05:51:03.667 >       yield from g
2025-07-02 05:51:03.674
2025-07-02 05:51:03.682 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:03.690 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:03.698
2025-07-02 05:51:03.705 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:03.715 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:03.729 alo = 353, ahi = 1101
2025-07-02 05:51:03.740 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:03.749 blo = 353, bhi = 1101
2025-07-02 05:51:03.759
2025-07-02 05:51:03.767 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:03.776 r"""
2025-07-02 05:51:03.784 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:03.795 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:03.804 synch point, and intraline difference marking is done on the
2025-07-02 05:51:03.813 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:03.821
2025-07-02 05:51:03.828 Example:
2025-07-02 05:51:03.834
2025-07-02 05:51:03.839 >>> d = Differ()
2025-07-02 05:51:03.844 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:03.849 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:03.854 >>> print(''.join(results), end="")
2025-07-02 05:51:03.858 - abcDefghiJkl
2025-07-02 05:51:03.868 + abcdefGhijkl
2025-07-02 05:51:03.878 """
2025-07-02 05:51:03.882
2025-07-02 05:51:03.887 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:03.892 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:03.896 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:03.901 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:03.906 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:03.910
2025-07-02 05:51:03.915 # search for the pair that matches best without being identical
2025-07-02 05:51:03.919 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:03.924 # on junk -- unless we have to)
2025-07-02 05:51:03.928 for j in range(blo, bhi):
2025-07-02 05:51:03.933 bj = b[j]
2025-07-02 05:51:03.937 cruncher.set_seq2(bj)
2025-07-02 05:51:03.942 for i in range(alo, ahi):
2025-07-02 05:51:03.946 ai = a[i]
2025-07-02 05:51:03.951 if ai == bj:
2025-07-02 05:51:03.955 if eqi is None:
2025-07-02 05:51:03.960 eqi, eqj = i, j
2025-07-02 05:51:03.964 continue
2025-07-02 05:51:03.972 cruncher.set_seq1(ai)
2025-07-02 05:51:03.980 # computing similarity is expensive, so use the quick
2025-07-02 05:51:03.987 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:03.995 # compares by a factor of 3.
2025-07-02 05:51:04.003 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:04.010 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:04.019 # of the computation is cached by cruncher
2025-07-02 05:51:04.032 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:04.042 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:04.051 cruncher.ratio() > best_ratio:
2025-07-02 05:51:04.060 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:04.067 if best_ratio < cutoff:
2025-07-02 05:51:04.074 # no non-identical "pretty close" pair
2025-07-02 05:51:04.086 if eqi is None:
2025-07-02 05:51:04.099 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:04.107 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:04.115 return
2025-07-02 05:51:04.123 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:04.128 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:04.134 else:
2025-07-02 05:51:04.142 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:04.154 eqi = None
2025-07-02 05:51:04.163
2025-07-02 05:51:04.173 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:04.181 # identical
2025-07-02 05:51:04.188
2025-07-02 05:51:04.194 # pump out diffs from before the synch point
2025-07-02 05:51:04.205 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:04.217
2025-07-02 05:51:04.226 # do intraline marking on the synch pair
2025-07-02 05:51:04.236 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:04.246 if eqi is None:
2025-07-02 05:51:04.255 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:04.266 atags = btags = ""
2025-07-02 05:51:04.276 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:04.285 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:04.292 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:04.298 if tag == 'replace':
2025-07-02 05:51:04.305 atags += '^' * la
2025-07-02 05:51:04.311 btags += '^' * lb
2025-07-02 05:51:04.319 elif tag == 'delete':
2025-07-02 05:51:04.328 atags += '-' * la
2025-07-02 05:51:04.340 elif tag == 'insert':
2025-07-02 05:51:04.350 btags += '+' * lb
2025-07-02 05:51:04.360 elif tag == 'equal':
2025-07-02 05:51:04.373 atags += ' ' * la
2025-07-02 05:51:04.382 btags += ' ' * lb
2025-07-02 05:51:04.394 else:
2025-07-02 05:51:04.404 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:04.418 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:04.430 else:
2025-07-02 05:51:04.440 # the synch pair is identical
2025-07-02 05:51:04.449 yield '  ' + aelt
2025-07-02 05:51:04.463
2025-07-02 05:51:04.477 # pump out diffs from after the synch point
2025-07-02 05:51:04.491 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:04.504
2025-07-02 05:51:04.515 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:04.525 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:04.535
2025-07-02 05:51:04.543 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:04.553 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:04.560 alo = 356, ahi = 1101
2025-07-02 05:51:04.567 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:04.573 blo = 356, bhi = 1101
2025-07-02 05:51:04.579
2025-07-02 05:51:04.586 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:04.595 g = []
2025-07-02 05:51:04.606 if alo < ahi:
2025-07-02 05:51:04.615 if blo < bhi:
2025-07-02 05:51:04.622 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:04.628 else:
2025-07-02 05:51:04.634 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:04.640 elif blo < bhi:
2025-07-02 05:51:04.647 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:04.655
2025-07-02 05:51:04.663 >       yield from g
2025-07-02 05:51:04.670
2025-07-02 05:51:04.678 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:04.685 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:04.691
2025-07-02 05:51:04.699 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:04.711 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:04.721 alo = 356, ahi = 1101
2025-07-02 05:51:04.727 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:04.731 blo = 356, bhi = 1101
2025-07-02 05:51:04.737
2025-07-02 05:51:04.745 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:04.752 r"""
2025-07-02 05:51:04.758 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:04.763 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:04.772 synch point, and intraline difference marking is done on the
2025-07-02 05:51:04.779 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:04.787
2025-07-02 05:51:04.797 Example:
2025-07-02 05:51:04.809
2025-07-02 05:51:04.821 >>> d = Differ()
2025-07-02 05:51:04.832 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:04.840 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:04.846 >>> print(''.join(results), end="")
2025-07-02 05:51:04.856 - abcDefghiJkl
2025-07-02 05:51:04.879 + abcdefGhijkl
2025-07-02 05:51:04.899 """
2025-07-02 05:51:04.907
2025-07-02 05:51:04.914 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:04.921 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:04.927 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:04.933 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:04.939 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:04.946
2025-07-02 05:51:04.953 # search for the pair that matches best without being identical
2025-07-02 05:51:04.959 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:04.966 # on junk -- unless we have to)
2025-07-02 05:51:04.978 for j in range(blo, bhi):
2025-07-02 05:51:04.987 bj = b[j]
2025-07-02 05:51:04.996 cruncher.set_seq2(bj)
2025-07-02 05:51:05.003 for i in range(alo, ahi):
2025-07-02 05:51:05.011 ai = a[i]
2025-07-02 05:51:05.018 if ai == bj:
2025-07-02 05:51:05.024 if eqi is None:
2025-07-02 05:51:05.030 eqi, eqj = i, j
2025-07-02 05:51:05.038 continue
2025-07-02 05:51:05.046 cruncher.set_seq1(ai)
2025-07-02 05:51:05.053 # computing similarity is expensive, so use the quick
2025-07-02 05:51:05.060 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:05.067 # compares by a factor of 3.
2025-07-02 05:51:05.074 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:05.083 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:05.090 # of the computation is cached by cruncher
2025-07-02 05:51:05.097 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:05.104 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:05.109 cruncher.ratio() > best_ratio:
2025-07-02 05:51:05.115 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:05.121 if best_ratio < cutoff:
2025-07-02 05:51:05.133 # no non-identical "pretty close" pair
2025-07-02 05:51:05.145 if eqi is None:
2025-07-02 05:51:05.155 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:05.163 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:05.172 return
2025-07-02 05:51:05.182 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:05.191 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:05.198 else:
2025-07-02 05:51:05.211 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:05.224 eqi = None
2025-07-02 05:51:05.234
2025-07-02 05:51:05.241 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:05.255 # identical
2025-07-02 05:51:05.267
2025-07-02 05:51:05.277 # pump out diffs from before the synch point
2025-07-02 05:51:05.289 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:05.299
2025-07-02 05:51:05.308 # do intraline marking on the synch pair
2025-07-02 05:51:05.316 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:05.322 if eqi is None:
2025-07-02 05:51:05.336 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:05.348 atags = btags = ""
2025-07-02 05:51:05.355 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:05.363 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:05.377 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:05.390 if tag == 'replace':
2025-07-02 05:51:05.402 atags += '^' * la
2025-07-02 05:51:05.410 btags += '^' * lb
2025-07-02 05:51:05.418 elif tag == 'delete':
2025-07-02 05:51:05.425 atags += '-' * la
2025-07-02 05:51:05.432 elif tag == 'insert':
2025-07-02 05:51:05.438 btags += '+' * lb
2025-07-02 05:51:05.444 elif tag == 'equal':
2025-07-02 05:51:05.451 atags += ' ' * la
2025-07-02 05:51:05.458 btags += ' ' * lb
2025-07-02 05:51:05.465 else:
2025-07-02 05:51:05.474 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:05.485 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:05.498 else:
2025-07-02 05:51:05.510 # the synch pair is identical
2025-07-02 05:51:05.520 yield '  ' + aelt
2025-07-02 05:51:05.529
2025-07-02 05:51:05.537 # pump out diffs from after the synch point
2025-07-02 05:51:05.550 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:05.560
2025-07-02 05:51:05.573 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:05.585 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:05.595
2025-07-02 05:51:05.606 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:05.620 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:05.630 alo = 357, ahi = 1101
2025-07-02 05:51:05.644 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:05.655 blo = 357, bhi = 1101
2025-07-02 05:51:05.669
2025-07-02 05:51:05.679 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:05.688 g = []
2025-07-02 05:51:05.695 if alo < ahi:
2025-07-02 05:51:05.703 if blo < bhi:
2025-07-02 05:51:05.717 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:05.728 else:
2025-07-02 05:51:05.737 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:05.744 elif blo < bhi:
2025-07-02 05:51:05.754 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:05.761
2025-07-02 05:51:05.772 >       yield from g
2025-07-02 05:51:05.778
2025-07-02 05:51:05.787 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:05.798 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:05.806
2025-07-02 05:51:05.813 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:05.820 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:05.826 alo = 357, ahi = 1101
2025-07-02 05:51:05.833 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:05.839 blo = 357, bhi = 1101
2025-07-02 05:51:05.847
2025-07-02 05:51:05.859 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:05.868 r"""
2025-07-02 05:51:05.875 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:05.881 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:05.889 synch point, and intraline difference marking is done on the
2025-07-02 05:51:05.896 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:05.903
2025-07-02 05:51:05.911 Example:
2025-07-02 05:51:05.922
2025-07-02 05:51:05.930 >>> d = Differ()
2025-07-02 05:51:05.937 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:05.944 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:05.952 >>> print(''.join(results), end="")
2025-07-02 05:51:05.959 - abcDefghiJkl
2025-07-02 05:51:05.973 + abcdefGhijkl
2025-07-02 05:51:05.987 """
2025-07-02 05:51:05.997
2025-07-02 05:51:06.007 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:06.015 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:06.028 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:06.037 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:06.045 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:06.051
2025-07-02 05:51:06.058 # search for the pair that matches best without being identical
2025-07-02 05:51:06.068 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:06.081 # on junk -- unless we have to)
2025-07-02 05:51:06.092 for j in range(blo, bhi):
2025-07-02 05:51:06.103 bj = b[j]
2025-07-02 05:51:06.109 cruncher.set_seq2(bj)
2025-07-02 05:51:06.117 for i in range(alo, ahi):
2025-07-02 05:51:06.124 ai = a[i]
2025-07-02 05:51:06.131 if ai == bj:
2025-07-02 05:51:06.138 if eqi is None:
2025-07-02 05:51:06.149 eqi, eqj = i, j
2025-07-02 05:51:06.159 continue
2025-07-02 05:51:06.166 cruncher.set_seq1(ai)
2025-07-02 05:51:06.172 # computing similarity is expensive, so use the quick
2025-07-02 05:51:06.179 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:06.186 # compares by a factor of 3.
2025-07-02 05:51:06.194 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:06.202 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:06.214 # of the computation is cached by cruncher
2025-07-02 05:51:06.223 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:06.231 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:06.237 cruncher.ratio() > best_ratio:
2025-07-02 05:51:06.242 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:06.247 if best_ratio < cutoff:
2025-07-02 05:51:06.256 # no non-identical "pretty close" pair
2025-07-02 05:51:06.265 if eqi is None:
2025-07-02 05:51:06.271 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:06.278 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:06.286 return
2025-07-02 05:51:06.297 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:06.308 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:06.315 else:
2025-07-02 05:51:06.322 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:06.330 eqi = None
2025-07-02 05:51:06.336
2025-07-02 05:51:06.344 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:06.351 # identical
2025-07-02 05:51:06.360
2025-07-02 05:51:06.371 # pump out diffs from before the synch point
2025-07-02 05:51:06.380 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:06.393
2025-07-02 05:51:06.401 # do intraline marking on the synch pair
2025-07-02 05:51:06.407 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:06.413 if eqi is None:
2025-07-02 05:51:06.421 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:06.427 atags = btags = ""
2025-07-02 05:51:06.434 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:06.441 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:06.448 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:06.455 if tag == 'replace':
2025-07-02 05:51:06.464 atags += '^' * la
2025-07-02 05:51:06.474 btags += '^' * lb
2025-07-02 05:51:06.482 elif tag == 'delete':
2025-07-02 05:51:06.488 atags += '-' * la
2025-07-02 05:51:06.494 elif tag == 'insert':
2025-07-02 05:51:06.499 btags += '+' * lb
2025-07-02 05:51:06.510 elif tag == 'equal':
2025-07-02 05:51:06.518 atags += ' ' * la
2025-07-02 05:51:06.525 btags += ' ' * lb
2025-07-02 05:51:06.531 else:
2025-07-02 05:51:06.538 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:06.549 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:06.559 else:
2025-07-02 05:51:06.566 # the synch pair is identical
2025-07-02 05:51:06.571 yield '  ' + aelt
2025-07-02 05:51:06.576
2025-07-02 05:51:06.582 # pump out diffs from after the synch point
2025-07-02 05:51:06.587 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:06.591
2025-07-02 05:51:06.596 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:06.601 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:06.605
2025-07-02 05:51:06.610 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:06.615 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:06.620 alo = 358, ahi = 1101
2025-07-02 05:51:06.625 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:06.630 blo = 358, bhi = 1101
2025-07-02 05:51:06.635
2025-07-02 05:51:06.640 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:06.645 g = []
2025-07-02 05:51:06.649 if alo < ahi:
2025-07-02 05:51:06.655 if blo < bhi:
2025-07-02 05:51:06.662 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:06.673 else:
2025-07-02 05:51:06.683 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:06.690 elif blo < bhi:
2025-07-02 05:51:06.696 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:06.707
2025-07-02 05:51:06.721 >       yield from g
2025-07-02 05:51:06.734
2025-07-02 05:51:06.746 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:06.755 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:06.762
2025-07-02 05:51:06.769 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:06.777 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:06.785 alo = 358, ahi = 1101
2025-07-02 05:51:06.796 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:06.806 blo = 358, bhi = 1101
2025-07-02 05:51:06.815
2025-07-02 05:51:06.822 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:06.828 r"""
2025-07-02 05:51:06.834 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:06.840 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:06.846 synch point, and intraline difference marking is done on the
2025-07-02 05:51:06.852 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:06.859
2025-07-02 05:51:06.867 Example:
2025-07-02 05:51:06.879
2025-07-02 05:51:06.888 >>> d = Differ()
2025-07-02 05:51:06.901 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:06.914 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:06.922 >>> print(''.join(results), end="")
2025-07-02 05:51:06.931 - abcDefghiJkl
2025-07-02 05:51:06.949 + abcdefGhijkl
2025-07-02 05:51:06.968 """
2025-07-02 05:51:06.974
2025-07-02 05:51:06.982 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:06.990 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:06.999 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:07.008 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:07.017 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:07.024
2025-07-02 05:51:07.030 # search for the pair that matches best without being identical
2025-07-02 05:51:07.038 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:07.044 # on junk -- unless we have to)
2025-07-02 05:51:07.051 for j in range(blo, bhi):
2025-07-02 05:51:07.057 bj = b[j]
2025-07-02 05:51:07.062 cruncher.set_seq2(bj)
2025-07-02 05:51:07.070 for i in range(alo, ahi):
2025-07-02 05:51:07.078 ai = a[i]
2025-07-02 05:51:07.086 if ai == bj:
2025-07-02 05:51:07.093 if eqi is None:
2025-07-02 05:51:07.100 eqi, eqj = i, j
2025-07-02 05:51:07.107 continue
2025-07-02 05:51:07.112 cruncher.set_seq1(ai)
2025-07-02 05:51:07.119 # computing similarity is expensive, so use the quick
2025-07-02 05:51:07.133 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:07.144 # compares by a factor of 3.
2025-07-02 05:51:07.157 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:07.168 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:07.176 # of the computation is cached by cruncher
2025-07-02 05:51:07.182 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:07.189 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:07.195 cruncher.ratio() > best_ratio:
2025-07-02 05:51:07.202 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:07.212 if best_ratio < cutoff:
2025-07-02 05:51:07.224 # no non-identical "pretty close" pair
2025-07-02 05:51:07.234 if eqi is None:
2025-07-02 05:51:07.248 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:07.259 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:07.267 return
2025-07-02 05:51:07.274 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:07.280 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:07.290 else:
2025-07-02 05:51:07.297 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:07.304 eqi = None
2025-07-02 05:51:07.311
2025-07-02 05:51:07.323 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:07.332 # identical
2025-07-02 05:51:07.339
2025-07-02 05:51:07.349 # pump out diffs from before the synch point
2025-07-02 05:51:07.360 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:07.375
2025-07-02 05:51:07.387 # do intraline marking on the synch pair
2025-07-02 05:51:07.397 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:07.410 if eqi is None:
2025-07-02 05:51:07.423 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:07.433 atags = btags = ""
2025-07-02 05:51:07.444 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:07.455 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:07.465 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:07.474 if tag == 'replace':
2025-07-02 05:51:07.481 atags += '^' * la
2025-07-02 05:51:07.495 btags += '^' * lb
2025-07-02 05:51:07.504 elif tag == 'delete':
2025-07-02 05:51:07.512 atags += '-' * la
2025-07-02 05:51:07.519 elif tag == 'insert':
2025-07-02 05:51:07.529 btags += '+' * lb
2025-07-02 05:51:07.540 elif tag == 'equal':
2025-07-02 05:51:07.548 atags += ' ' * la
2025-07-02 05:51:07.556 btags += ' ' * lb
2025-07-02 05:51:07.563 else:
2025-07-02 05:51:07.569 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:07.575 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:07.581 else:
2025-07-02 05:51:07.593 # the synch pair is identical
2025-07-02 05:51:07.603 yield '  ' + aelt
2025-07-02 05:51:07.611
2025-07-02 05:51:07.618 # pump out diffs from after the synch point
2025-07-02 05:51:07.626 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:07.632
2025-07-02 05:51:07.644 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:07.656 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:07.663
2025-07-02 05:51:07.671 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:07.678 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:07.685 alo = 359, ahi = 1101
2025-07-02 05:51:07.692 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:07.698 blo = 359, bhi = 1101
2025-07-02 05:51:07.703
2025-07-02 05:51:07.709 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:07.715 g = []
2025-07-02 05:51:07.729 if alo < ahi:
2025-07-02 05:51:07.750 if blo < bhi:
2025-07-02 05:51:07.760 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:07.766 else:
2025-07-02 05:51:07.773 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:07.779 elif blo < bhi:
2025-07-02 05:51:07.785 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:07.791
2025-07-02 05:51:07.798 >       yield from g
2025-07-02 05:51:07.807
2025-07-02 05:51:07.814 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:07.822 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:07.830
2025-07-02 05:51:07.837 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:07.845 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:07.851 alo = 359, ahi = 1101
2025-07-02 05:51:07.859 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:07.868 blo = 359, bhi = 1101
2025-07-02 05:51:07.880
2025-07-02 05:51:07.889 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:07.899 r"""
2025-07-02 05:51:07.911 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:07.919 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:07.926 synch point, and intraline difference marking is done on the
2025-07-02 05:51:07.938 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:07.951
2025-07-02 05:51:07.961 Example:
2025-07-02 05:51:07.970
2025-07-02 05:51:07.979 >>> d = Differ()
2025-07-02 05:51:07.988 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:07.995 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:08.003 >>> print(''.join(results), end="")
2025-07-02 05:51:08.014 - abcDefghiJkl
2025-07-02 05:51:08.031 + abcdefGhijkl
2025-07-02 05:51:08.055 """
2025-07-02 05:51:08.065
2025-07-02 05:51:08.078 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:08.091 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:08.100 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:08.108 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:08.122 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:08.129
2025-07-02 05:51:08.136 # search for the pair that matches best without being identical
2025-07-02 05:51:08.143 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:08.151 # on junk -- unless we have to)
2025-07-02 05:51:08.163 for j in range(blo, bhi):
2025-07-02 05:51:08.170 bj = b[j]
2025-07-02 05:51:08.176 cruncher.set_seq2(bj)
2025-07-02 05:51:08.183 for i in range(alo, ahi):
2025-07-02 05:51:08.190 ai = a[i]
2025-07-02 05:51:08.196 if ai == bj:
2025-07-02 05:51:08.203 if eqi is None:
2025-07-02 05:51:08.211 eqi, eqj = i, j
2025-07-02 05:51:08.222 continue
2025-07-02 05:51:08.230 cruncher.set_seq1(ai)
2025-07-02 05:51:08.238 # computing similarity is expensive, so use the quick
2025-07-02 05:51:08.250 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:08.258 # compares by a factor of 3.
2025-07-02 05:51:08.270 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:08.279 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:08.291 # of the computation is cached by cruncher
2025-07-02 05:51:08.300 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:08.308 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:08.316 cruncher.ratio() > best_ratio:
2025-07-02 05:51:08.323 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:08.329 if best_ratio < cutoff:
2025-07-02 05:51:08.336 # no non-identical "pretty close" pair
2025-07-02 05:51:08.343 if eqi is None:
2025-07-02 05:51:08.353 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:08.366 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:08.375 return
2025-07-02 05:51:08.382 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:08.395 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:08.406 else:
2025-07-02 05:51:08.415 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:08.423 eqi = None
2025-07-02 05:51:08.429
2025-07-02 05:51:08.441 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:08.452 # identical
2025-07-02 05:51:08.461
2025-07-02 05:51:08.468 # pump out diffs from before the synch point
2025-07-02 05:51:08.475 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:08.483
2025-07-02 05:51:08.498 # do intraline marking on the synch pair
2025-07-02 05:51:08.510 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:08.521 if eqi is None:
2025-07-02 05:51:08.532 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:08.544 atags = btags = ""
2025-07-02 05:51:08.554 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:08.563 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:08.571 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:08.577 if tag == 'replace':
2025-07-02 05:51:08.583 atags += '^' * la
2025-07-02 05:51:08.592 btags += '^' * lb
2025-07-02 05:51:08.603 elif tag == 'delete':
2025-07-02 05:51:08.613 atags += '-' * la
2025-07-02 05:51:08.621 elif tag == 'insert':
2025-07-02 05:51:08.629 btags += '+' * lb
2025-07-02 05:51:08.636 elif tag == 'equal':
2025-07-02 05:51:08.643 atags += ' ' * la
2025-07-02 05:51:08.652 btags += ' ' * lb
2025-07-02 05:51:08.659 else:
2025-07-02 05:51:08.672 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:08.681 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:08.694 else:
2025-07-02 05:51:08.705 # the synch pair is identical
2025-07-02 05:51:08.716 yield '  ' + aelt
2025-07-02 05:51:08.724
2025-07-02 05:51:08.731 # pump out diffs from after the synch point
2025-07-02 05:51:08.746 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:08.758
2025-07-02 05:51:08.769 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:08.779 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:08.787
2025-07-02 05:51:08.794 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:08.803 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:08.813 alo = 360, ahi = 1101
2025-07-02 05:51:08.823 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:08.830 blo = 360, bhi = 1101
2025-07-02 05:51:08.836
2025-07-02 05:51:08.843 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:08.849 g = []
2025-07-02 05:51:08.855 if alo < ahi:
2025-07-02 05:51:08.860 if blo < bhi:
2025-07-02 05:51:08.866 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:08.871 else:
2025-07-02 05:51:08.876 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:08.882 elif blo < bhi:
2025-07-02 05:51:08.891 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:08.898
2025-07-02 05:51:08.903 >       yield from g
2025-07-02 05:51:08.911
2025-07-02 05:51:08.922 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:08.932 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:08.945
2025-07-02 05:51:08.954 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:08.963 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:08.976 alo = 360, ahi = 1101
2025-07-02 05:51:08.990 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:09.001 blo = 360, bhi = 1101
2025-07-02 05:51:09.013
2025-07-02 05:51:09.024 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:09.037 r"""
2025-07-02 05:51:09.045 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:09.052 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:09.058 synch point, and intraline difference marking is done on the
2025-07-02 05:51:09.065 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:09.072
2025-07-02 05:51:09.078 Example:
2025-07-02 05:51:09.084
2025-07-02 05:51:09.090 >>> d = Differ()
2025-07-02 05:51:09.097 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:09.103 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:09.109 >>> print(''.join(results), end="")
2025-07-02 05:51:09.120 - abcDefghiJkl
2025-07-02 05:51:09.140 + abcdefGhijkl
2025-07-02 05:51:09.160 """
2025-07-02 05:51:09.167
2025-07-02 05:51:09.175 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:09.182 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:09.192 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:09.204 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:09.212 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:09.219
2025-07-02 05:51:09.225 # search for the pair that matches best without being identical
2025-07-02 05:51:09.236 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:09.247 # on junk -- unless we have to)
2025-07-02 05:51:09.257 for j in range(blo, bhi):
2025-07-02 05:51:09.267 bj = b[j]
2025-07-02 05:51:09.278 cruncher.set_seq2(bj)
2025-07-02 05:51:09.289 for i in range(alo, ahi):
2025-07-02 05:51:09.297 ai = a[i]
2025-07-02 05:51:09.305 if ai == bj:
2025-07-02 05:51:09.312 if eqi is None:
2025-07-02 05:51:09.319 eqi, eqj = i, j
2025-07-02 05:51:09.331 continue
2025-07-02 05:51:09.340 cruncher.set_seq1(ai)
2025-07-02 05:51:09.348 # computing similarity is expensive, so use the quick
2025-07-02 05:51:09.356 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:09.363 # compares by a factor of 3.
2025-07-02 05:51:09.373 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:09.385 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:09.397 # of the computation is cached by cruncher
2025-07-02 05:51:09.409 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:09.422 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:09.432 cruncher.ratio() > best_ratio:
2025-07-02 05:51:09.443 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:09.455 if best_ratio < cutoff:
2025-07-02 05:51:09.466 # no non-identical "pretty close" pair
2025-07-02 05:51:09.475 if eqi is None:
2025-07-02 05:51:09.487 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:09.497 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:09.505 return
2025-07-02 05:51:09.516 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:09.525 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:09.532 else:
2025-07-02 05:51:09.540 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:09.546 eqi = None
2025-07-02 05:51:09.556
2025-07-02 05:51:09.567 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:09.574 # identical
2025-07-02 05:51:09.581
2025-07-02 05:51:09.588 # pump out diffs from before the synch point
2025-07-02 05:51:09.594 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:09.600
2025-07-02 05:51:09.606 # do intraline marking on the synch pair
2025-07-02 05:51:09.612 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:09.618 if eqi is None:
2025-07-02 05:51:09.624 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:09.630 atags = btags = ""
2025-07-02 05:51:09.641 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:09.654 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:09.664 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:09.676 if tag == 'replace':
2025-07-02 05:51:09.686 atags += '^' * la
2025-07-02 05:51:09.698 btags += '^' * lb
2025-07-02 05:51:09.708 elif tag == 'delete':
2025-07-02 05:51:09.716 atags += '-' * la
2025-07-02 05:51:09.723 elif tag == 'insert':
2025-07-02 05:51:09.731 btags += '+' * lb
2025-07-02 05:51:09.742 elif tag == 'equal':
2025-07-02 05:51:09.751 atags += ' ' * la
2025-07-02 05:51:09.759 btags += ' ' * lb
2025-07-02 05:51:09.767 else:
2025-07-02 05:51:09.776 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:09.788 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:09.800 else:
2025-07-02 05:51:09.809 # the synch pair is identical
2025-07-02 05:51:09.816 yield '  ' + aelt
2025-07-02 05:51:09.823
2025-07-02 05:51:09.830 # pump out diffs from after the synch point
2025-07-02 05:51:09.839 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:09.850
2025-07-02 05:51:09.860 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:09.868 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:09.875
2025-07-02 05:51:09.887 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:09.897 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:09.904 alo = 361, ahi = 1101
2025-07-02 05:51:09.914 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:09.925 blo = 361, bhi = 1101
2025-07-02 05:51:09.935
2025-07-02 05:51:09.944 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:09.951 g = []
2025-07-02 05:51:09.959 if alo < ahi:
2025-07-02 05:51:09.965 if blo < bhi:
2025-07-02 05:51:09.971 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:09.977 else:
2025-07-02 05:51:09.983 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:09.990 elif blo < bhi:
2025-07-02 05:51:10.002 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:10.012
2025-07-02 05:51:10.025 >       yield from g
2025-07-02 05:51:10.036
2025-07-02 05:51:10.047 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:10.061 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:10.073
2025-07-02 05:51:10.082 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:10.092 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:10.099 alo = 361, ahi = 1101
2025-07-02 05:51:10.108 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:10.114 blo = 361, bhi = 1101
2025-07-02 05:51:10.121
2025-07-02 05:51:10.127 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:10.133 r"""
2025-07-02 05:51:10.138 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:10.143 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:10.148 synch point, and intraline difference marking is done on the
2025-07-02 05:51:10.153 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:10.158
2025-07-02 05:51:10.164 Example:
2025-07-02 05:51:10.169
2025-07-02 05:51:10.174 >>> d = Differ()
2025-07-02 05:51:10.180 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:10.185 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:10.190 >>> print(''.join(results), end="")
2025-07-02 05:51:10.195 - abcDefghiJkl
2025-07-02 05:51:10.205 + abcdefGhijkl
2025-07-02 05:51:10.215 """
2025-07-02 05:51:10.224
2025-07-02 05:51:10.231 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:10.239 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:10.248 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:10.260 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:10.271 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:10.278
2025-07-02 05:51:10.283 # search for the pair that matches best without being identical
2025-07-02 05:51:10.289 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:10.293 # on junk -- unless we have to)
2025-07-02 05:51:10.302 for j in range(blo, bhi):
2025-07-02 05:51:10.309 bj = b[j]
2025-07-02 05:51:10.316 cruncher.set_seq2(bj)
2025-07-02 05:51:10.321 for i in range(alo, ahi):
2025-07-02 05:51:10.330 ai = a[i]
2025-07-02 05:51:10.335 if ai == bj:
2025-07-02 05:51:10.341 if eqi is None:
2025-07-02 05:51:10.346 eqi, eqj = i, j
2025-07-02 05:51:10.351 continue
2025-07-02 05:51:10.356 cruncher.set_seq1(ai)
2025-07-02 05:51:10.362 # computing similarity is expensive, so use the quick
2025-07-02 05:51:10.373 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:10.383 # compares by a factor of 3.
2025-07-02 05:51:10.390 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:10.396 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:10.408 # of the computation is cached by cruncher
2025-07-02 05:51:10.423 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:10.434 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:10.446 cruncher.ratio() > best_ratio:
2025-07-02 05:51:10.454 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:10.460 if best_ratio < cutoff:
2025-07-02 05:51:10.466 # no non-identical "pretty close" pair
2025-07-02 05:51:10.472 if eqi is None:
2025-07-02 05:51:10.477 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:10.483 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:10.491 return
2025-07-02 05:51:10.496 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:10.501 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:10.506 else:
2025-07-02 05:51:10.512 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:10.519 eqi = None
2025-07-02 05:51:10.527
2025-07-02 05:51:10.540 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:10.551 # identical
2025-07-02 05:51:10.560
2025-07-02 05:51:10.568 # pump out diffs from before the synch point
2025-07-02 05:51:10.575 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:10.581
2025-07-02 05:51:10.588 # do intraline marking on the synch pair
2025-07-02 05:51:10.594 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:10.603 if eqi is None:
2025-07-02 05:51:10.616 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:10.632 atags = btags = ""
2025-07-02 05:51:10.641 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:10.649 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:10.656 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:10.662 if tag == 'replace':
2025-07-02 05:51:10.667 atags += '^' * la
2025-07-02 05:51:10.674 btags += '^' * lb
2025-07-02 05:51:10.681 elif tag == 'delete':
2025-07-02 05:51:10.688 atags += '-' * la
2025-07-02 05:51:10.694 elif tag == 'insert':
2025-07-02 05:51:10.703 btags += '+' * lb
2025-07-02 05:51:10.710 elif tag == 'equal':
2025-07-02 05:51:10.717 atags += ' ' * la
2025-07-02 05:51:10.722 btags += ' ' * lb
2025-07-02 05:51:10.730 else:
2025-07-02 05:51:10.744 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:10.753 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:10.761 else:
2025-07-02 05:51:10.767 # the synch pair is identical
2025-07-02 05:51:10.775 yield '  ' + aelt
2025-07-02 05:51:10.786
2025-07-02 05:51:10.798 # pump out diffs from after the synch point
2025-07-02 05:51:10.808 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:10.817
2025-07-02 05:51:10.825 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:10.838 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:10.850
2025-07-02 05:51:10.860 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:10.871 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:10.883 alo = 362, ahi = 1101
2025-07-02 05:51:10.895 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:10.910 blo = 362, bhi = 1101
2025-07-02 05:51:10.921
2025-07-02 05:51:10.929 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:10.936 g = []
2025-07-02 05:51:10.943 if alo < ahi:
2025-07-02 05:51:10.950 if blo < bhi:
2025-07-02 05:51:10.956 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:10.962 else:
2025-07-02 05:51:10.968 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:10.974 elif blo < bhi:
2025-07-02 05:51:10.985 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:10.996
2025-07-02 05:51:11.005 >       yield from g
2025-07-02 05:51:11.012
2025-07-02 05:51:11.018 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:11.025 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:11.035
2025-07-02 05:51:11.045 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:11.057 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:11.066 alo = 362, ahi = 1101
2025-07-02 05:51:11.081 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:11.092 blo = 362, bhi = 1101
2025-07-02 05:51:11.101
2025-07-02 05:51:11.109 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:11.116 r"""
2025-07-02 05:51:11.122 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:11.128 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:11.135 synch point, and intraline difference marking is done on the
2025-07-02 05:51:11.142 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:11.151
2025-07-02 05:51:11.165 Example:
2025-07-02 05:51:11.175
2025-07-02 05:51:11.187 >>> d = Differ()
2025-07-02 05:51:11.196 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:11.204 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:11.211 >>> print(''.join(results), end="")
2025-07-02 05:51:11.217 - abcDefghiJkl
2025-07-02 05:51:11.229 + abcdefGhijkl
2025-07-02 05:51:11.239 """
2025-07-02 05:51:11.247
2025-07-02 05:51:11.258 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:11.268 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:11.279 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:11.291 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:11.302 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:11.314
2025-07-02 05:51:11.324 # search for the pair that matches best without being identical
2025-07-02 05:51:11.332 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:11.339 # on junk -- unless we have to)
2025-07-02 05:51:11.346 for j in range(blo, bhi):
2025-07-02 05:51:11.352 bj = b[j]
2025-07-02 05:51:11.358 cruncher.set_seq2(bj)
2025-07-02 05:51:11.364 for i in range(alo, ahi):
2025-07-02 05:51:11.376 ai = a[i]
2025-07-02 05:51:11.384 if ai == bj:
2025-07-02 05:51:11.391 if eqi is None:
2025-07-02 05:51:11.397 eqi, eqj = i, j
2025-07-02 05:51:11.403 continue
2025-07-02 05:51:11.410 cruncher.set_seq1(ai)
2025-07-02 05:51:11.418 # computing similarity is expensive, so use the quick
2025-07-02 05:51:11.429 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:11.441 # compares by a factor of 3.
2025-07-02 05:51:11.452 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:11.463 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:11.476 # of the computation is cached by cruncher
2025-07-02 05:51:11.486 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:11.494 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:11.501 cruncher.ratio() > best_ratio:
2025-07-02 05:51:11.509 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:11.517 if best_ratio < cutoff:
2025-07-02 05:51:11.525 # no non-identical "pretty close" pair
2025-07-02 05:51:11.532 if eqi is None:
2025-07-02 05:51:11.540 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:11.549 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:11.556 return
2025-07-02 05:51:11.564 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:11.572 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:11.583 else:
2025-07-02 05:51:11.593 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:11.599 eqi = None
2025-07-02 05:51:11.605
2025-07-02 05:51:11.610 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:11.615 # identical
2025-07-02 05:51:11.620
2025-07-02 05:51:11.626 # pump out diffs from before the synch point
2025-07-02 05:51:11.637 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:11.647
2025-07-02 05:51:11.657 # do intraline marking on the synch pair
2025-07-02 05:51:11.665 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:11.677 if eqi is None:
2025-07-02 05:51:11.691 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:11.702 atags = btags = ""
2025-07-02 05:51:11.712 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:11.723 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:11.733 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:11.745 if tag == 'replace':
2025-07-02 05:51:11.755 atags += '^' * la
2025-07-02 05:51:11.764 btags += '^' * lb
2025-07-02 05:51:11.772 elif tag == 'delete':
2025-07-02 05:51:11.778 atags += '-' * la
2025-07-02 05:51:11.785 elif tag == 'insert':
2025-07-02 05:51:11.791 btags += '+' * lb
2025-07-02 05:51:11.798 elif tag == 'equal':
2025-07-02 05:51:11.807 atags += ' ' * la
2025-07-02 05:51:11.818 btags += ' ' * lb
2025-07-02 05:51:11.827 else:
2025-07-02 05:51:11.834 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:11.845 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:11.856 else:
2025-07-02 05:51:11.864 # the synch pair is identical
2025-07-02 05:51:11.878 yield '  ' + aelt
2025-07-02 05:51:11.890
2025-07-02 05:51:11.898 # pump out diffs from after the synch point
2025-07-02 05:51:11.904 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:11.910
2025-07-02 05:51:11.915 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:11.920 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:11.926
2025-07-02 05:51:11.937 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:11.949 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:11.961 alo = 363, ahi = 1101
2025-07-02 05:51:11.971 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:11.979 blo = 363, bhi = 1101
2025-07-02 05:51:11.987
2025-07-02 05:51:11.994 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:12.000 g = []
2025-07-02 05:51:12.008 if alo < ahi:
2025-07-02 05:51:12.020 if blo < bhi:
2025-07-02 05:51:12.032 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:12.043 else:
2025-07-02 05:51:12.055 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:12.065 elif blo < bhi:
2025-07-02 05:51:12.072 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:12.079
2025-07-02 05:51:12.085 >       yield from g
2025-07-02 05:51:12.091
2025-07-02 05:51:12.098 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:12.103 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:12.108
2025-07-02 05:51:12.115 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:12.122 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:12.135 alo = 363, ahi = 1101
2025-07-02 05:51:12.146 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:12.158 blo = 363, bhi = 1101
2025-07-02 05:51:12.171
2025-07-02 05:51:12.179 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:12.190 r"""
2025-07-02 05:51:12.201 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:12.210 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:12.219 synch point, and intraline difference marking is done on the
2025-07-02 05:51:12.230 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:12.239
2025-07-02 05:51:12.247 Example:
2025-07-02 05:51:12.256
2025-07-02 05:51:12.268 >>> d = Differ()
2025-07-02 05:51:12.279 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:12.290 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:12.299 >>> print(''.join(results), end="")
2025-07-02 05:51:12.307 - abcDefghiJkl
2025-07-02 05:51:12.320 + abcdefGhijkl
2025-07-02 05:51:12.335 """
2025-07-02 05:51:12.346
2025-07-02 05:51:12.355 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:12.365 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:12.372 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:12.378 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:12.384 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:12.391
2025-07-02 05:51:12.403 # search for the pair that matches best without being identical
2025-07-02 05:51:12.412 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:12.418 # on junk -- unless we have to)
2025-07-02 05:51:12.425 for j in range(blo, bhi):
2025-07-02 05:51:12.430 bj = b[j]
2025-07-02 05:51:12.444 cruncher.set_seq2(bj)
2025-07-02 05:51:12.456 for i in range(alo, ahi):
2025-07-02 05:51:12.466 ai = a[i]
2025-07-02 05:51:12.478 if ai == bj:
2025-07-02 05:51:12.489 if eqi is None:
2025-07-02 05:51:12.497 eqi, eqj = i, j
2025-07-02 05:51:12.509 continue
2025-07-02 05:51:12.518 cruncher.set_seq1(ai)
2025-07-02 05:51:12.527 # computing similarity is expensive, so use the quick
2025-07-02 05:51:12.534 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:12.543 # compares by a factor of 3.
2025-07-02 05:51:12.555 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:12.565 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:12.573 # of the computation is cached by cruncher
2025-07-02 05:51:12.579 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:12.586 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:12.593 cruncher.ratio() > best_ratio:
2025-07-02 05:51:12.604 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:12.615 if best_ratio < cutoff:
2025-07-02 05:51:12.627 # no non-identical "pretty close" pair
2025-07-02 05:51:12.637 if eqi is None:
2025-07-02 05:51:12.648 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:12.660 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:12.669 return
2025-07-02 05:51:12.676 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:12.683 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:12.690 else:
2025-07-02 05:51:12.702 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:12.711 eqi = None
2025-07-02 05:51:12.719
2025-07-02 05:51:12.727 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:12.734 # identical
2025-07-02 05:51:12.746
2025-07-02 05:51:12.754 # pump out diffs from before the synch point
2025-07-02 05:51:12.761 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:12.768
2025-07-02 05:51:12.774 # do intraline marking on the synch pair
2025-07-02 05:51:12.780 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:12.786 if eqi is None:
2025-07-02 05:51:12.792 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:12.804 atags = btags = ""
2025-07-02 05:51:12.814 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:12.822 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:12.835 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:12.845 if tag == 'replace':
2025-07-02 05:51:12.852 atags += '^' * la
2025-07-02 05:51:12.866 btags += '^' * lb
2025-07-02 05:51:12.877 elif tag == 'delete':
2025-07-02 05:51:12.885 atags += '-' * la
2025-07-02 05:51:12.892 elif tag == 'insert':
2025-07-02 05:51:12.899 btags += '+' * lb
2025-07-02 05:51:12.907 elif tag == 'equal':
2025-07-02 05:51:12.917 atags += ' ' * la
2025-07-02 05:51:12.925 btags += ' ' * lb
2025-07-02 05:51:12.932 else:
2025-07-02 05:51:12.940 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:12.950 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:12.957 else:
2025-07-02 05:51:12.963 # the synch pair is identical
2025-07-02 05:51:12.972 yield '  ' + aelt
2025-07-02 05:51:12.985
2025-07-02 05:51:12.993 # pump out diffs from after the synch point
2025-07-02 05:51:13.000 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:13.007
2025-07-02 05:51:13.014 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:13.021 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:13.028
2025-07-02 05:51:13.036 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:13.049 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:13.058 alo = 364, ahi = 1101
2025-07-02 05:51:13.065 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:13.078 blo = 364, bhi = 1101
2025-07-02 05:51:13.090
2025-07-02 05:51:13.105 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:13.115 g = []
2025-07-02 05:51:13.122 if alo < ahi:
2025-07-02 05:51:13.127 if blo < bhi:
2025-07-02 05:51:13.133 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:13.141 else:
2025-07-02 05:51:13.154 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:13.165 elif blo < bhi:
2025-07-02 05:51:13.174 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:13.181
2025-07-02 05:51:13.188 >       yield from g
2025-07-02 05:51:13.193
2025-07-02 05:51:13.199 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:13.204 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:13.209
2025-07-02 05:51:13.214 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:13.221 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:13.227 alo = 364, ahi = 1101
2025-07-02 05:51:13.234 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:13.245 blo = 364, bhi = 1101
2025-07-02 05:51:13.256
2025-07-02 05:51:13.264 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:13.271 r"""
2025-07-02 05:51:13.284 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:13.297 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:13.307 synch point, and intraline difference marking is done on the
2025-07-02 05:51:13.316 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:13.323
2025-07-02 05:51:13.337 Example:
2025-07-02 05:51:13.348
2025-07-02 05:51:13.359 >>> d = Differ()
2025-07-02 05:51:13.372 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:13.383 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:13.394 >>> print(''.join(results), end="")
2025-07-02 05:51:13.409 - abcDefghiJkl
2025-07-02 05:51:13.428 + abcdefGhijkl
2025-07-02 05:51:13.443 """
2025-07-02 05:51:13.452
2025-07-02 05:51:13.467 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:13.479 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:13.496 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:13.504 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:13.513 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:13.519
2025-07-02 05:51:13.526 # search for the pair that matches best without being identical
2025-07-02 05:51:13.532 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:13.538 # on junk -- unless we have to)
2025-07-02 05:51:13.543 for j in range(blo, bhi):
2025-07-02 05:51:13.548 bj = b[j]
2025-07-02 05:51:13.554 cruncher.set_seq2(bj)
2025-07-02 05:51:13.559 for i in range(alo, ahi):
2025-07-02 05:51:13.564 ai = a[i]
2025-07-02 05:51:13.570 if ai == bj:
2025-07-02 05:51:13.576 if eqi is None:
2025-07-02 05:51:13.582 eqi, eqj = i, j
2025-07-02 05:51:13.588 continue
2025-07-02 05:51:13.595 cruncher.set_seq1(ai)
2025-07-02 05:51:13.603 # computing similarity is expensive, so use the quick
2025-07-02 05:51:13.615 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:13.624 # compares by a factor of 3.
2025-07-02 05:51:13.631 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:13.638 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:13.647 # of the computation is cached by cruncher
2025-07-02 05:51:13.658 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:13.670 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:13.680 cruncher.ratio() > best_ratio:
2025-07-02 05:51:13.692 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:13.705 if best_ratio < cutoff:
2025-07-02 05:51:13.712 # no non-identical "pretty close" pair
2025-07-02 05:51:13.717 if eqi is None:
2025-07-02 05:51:13.723 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:13.732 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:13.744 return
2025-07-02 05:51:13.753 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:13.766 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:13.776 else:
2025-07-02 05:51:13.784 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:13.791 eqi = None
2025-07-02 05:51:13.797
2025-07-02 05:51:13.803 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:13.809 # identical
2025-07-02 05:51:13.814
2025-07-02 05:51:13.823 # pump out diffs from before the synch point
2025-07-02 05:51:13.834 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:13.843
2025-07-02 05:51:13.855 # do intraline marking on the synch pair
2025-07-02 05:51:13.866 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:13.874 if eqi is None:
2025-07-02 05:51:13.882 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:13.890 atags = btags = ""
2025-07-02 05:51:13.899 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:13.913 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:13.924 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:13.932 if tag == 'replace':
2025-07-02 05:51:13.940 atags += '^' * la
2025-07-02 05:51:13.950 btags += '^' * lb
2025-07-02 05:51:13.962 elif tag == 'delete':
2025-07-02 05:51:13.975 atags += '-' * la
2025-07-02 05:51:13.989 elif tag == 'insert':
2025-07-02 05:51:14.002 btags += '+' * lb
2025-07-02 05:51:14.012 elif tag == 'equal':
2025-07-02 05:51:14.020 atags += ' ' * la
2025-07-02 05:51:14.034 btags += ' ' * lb
2025-07-02 05:51:14.047 else:
2025-07-02 05:51:14.058 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:14.070 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:14.081 else:
2025-07-02 05:51:14.093 # the synch pair is identical
2025-07-02 05:51:14.103 yield '  ' + aelt
2025-07-02 05:51:14.113
2025-07-02 05:51:14.120 # pump out diffs from after the synch point
2025-07-02 05:51:14.132 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:14.141
2025-07-02 05:51:14.147 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:14.154 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:14.160
2025-07-02 05:51:14.166 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:14.175 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:14.187 alo = 365, ahi = 1101
2025-07-02 05:51:14.198 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:14.205 blo = 365, bhi = 1101
2025-07-02 05:51:14.211
2025-07-02 05:51:14.219 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:14.227 g = []
2025-07-02 05:51:14.236 if alo < ahi:
2025-07-02 05:51:14.244 if blo < bhi:
2025-07-02 05:51:14.257 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:14.268 else:
2025-07-02 05:51:14.279 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:14.289 elif blo < bhi:
2025-07-02 05:51:14.297 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:14.305
2025-07-02 05:51:14.312 >       yield from g
2025-07-02 05:51:14.318
2025-07-02 05:51:14.325 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:14.332 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:14.338
2025-07-02 05:51:14.345 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:14.355 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:14.365 alo = 365, ahi = 1101
2025-07-02 05:51:14.373 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:14.381 blo = 365, bhi = 1101
2025-07-02 05:51:14.389
2025-07-02 05:51:14.397 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:14.407 r"""
2025-07-02 05:51:14.415 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:14.422 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:14.432 synch point, and intraline difference marking is done on the
2025-07-02 05:51:14.441 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:14.449
2025-07-02 05:51:14.455 Example:
2025-07-02 05:51:14.462
2025-07-02 05:51:14.474 >>> d = Differ()
2025-07-02 05:51:14.483 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:14.491 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:14.499 >>> print(''.join(results), end="")
2025-07-02 05:51:14.506 - abcDefghiJkl
2025-07-02 05:51:14.527 + abcdefGhijkl
2025-07-02 05:51:14.542 """
2025-07-02 05:51:14.553
2025-07-02 05:51:14.562 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:14.570 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:14.578 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:14.589 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:14.600 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:14.611
2025-07-02 05:51:14.620 # search for the pair that matches best without being identical
2025-07-02 05:51:14.628 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:14.635 # on junk -- unless we have to)
2025-07-02 05:51:14.642 for j in range(blo, bhi):
2025-07-02 05:51:14.650 bj = b[j]
2025-07-02 05:51:14.658 cruncher.set_seq2(bj)
2025-07-02 05:51:14.665 for i in range(alo, ahi):
2025-07-02 05:51:14.671 ai = a[i]
2025-07-02 05:51:14.683 if ai == bj:
2025-07-02 05:51:14.692 if eqi is None:
2025-07-02 05:51:14.702 eqi, eqj = i, j
2025-07-02 05:51:14.709 continue
2025-07-02 05:51:14.718 cruncher.set_seq1(ai)
2025-07-02 05:51:14.730 # computing similarity is expensive, so use the quick
2025-07-02 05:51:14.740 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:14.748 # compares by a factor of 3.
2025-07-02 05:51:14.755 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:14.761 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:14.768 # of the computation is cached by cruncher
2025-07-02 05:51:14.775 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:14.783 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:14.796 cruncher.ratio() > best_ratio:
2025-07-02 05:51:14.805 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:14.812 if best_ratio < cutoff:
2025-07-02 05:51:14.819 # no non-identical "pretty close" pair
2025-07-02 05:51:14.826 if eqi is None:
2025-07-02 05:51:14.839 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:14.847 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:14.855 return
2025-07-02 05:51:14.863 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:14.871 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:14.878 else:
2025-07-02 05:51:14.889 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:14.899 eqi = None
2025-07-02 05:51:14.910
2025-07-02 05:51:14.918 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:14.930 # identical
2025-07-02 05:51:14.943
2025-07-02 05:51:14.956 # pump out diffs from before the synch point
2025-07-02 05:51:14.967 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:14.977
2025-07-02 05:51:14.990 # do intraline marking on the synch pair
2025-07-02 05:51:15.002 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:15.013 if eqi is None:
2025-07-02 05:51:15.026 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:15.036 atags = btags = ""
2025-07-02 05:51:15.046 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:15.055 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:15.062 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:15.073 if tag == 'replace':
2025-07-02 05:51:15.082 atags += '^' * la
2025-07-02 05:51:15.089 btags += '^' * lb
2025-07-02 05:51:15.096 elif tag == 'delete':
2025-07-02 05:51:15.103 atags += '-' * la
2025-07-02 05:51:15.110 elif tag == 'insert':
2025-07-02 05:51:15.120 btags += '+' * lb
2025-07-02 05:51:15.132 elif tag == 'equal':
2025-07-02 05:51:15.140 atags += ' ' * la
2025-07-02 05:51:15.150 btags += ' ' * lb
2025-07-02 05:51:15.162 else:
2025-07-02 05:51:15.173 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:15.184 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:15.193 else:
2025-07-02 05:51:15.205 # the synch pair is identical
2025-07-02 05:51:15.213 yield '  ' + aelt
2025-07-02 05:51:15.219
2025-07-02 05:51:15.229 # pump out diffs from after the synch point
2025-07-02 05:51:15.235 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:15.242
2025-07-02 05:51:15.248 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:15.255 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:15.261
2025-07-02 05:51:15.267 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:15.274 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:15.281 alo = 366, ahi = 1101
2025-07-02 05:51:15.290 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:15.301 blo = 366, bhi = 1101
2025-07-02 05:51:15.310
2025-07-02 05:51:15.317 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:15.324 g = []
2025-07-02 05:51:15.331 if alo < ahi:
2025-07-02 05:51:15.337 if blo < bhi:
2025-07-02 05:51:15.343 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:15.348 else:
2025-07-02 05:51:15.354 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:15.360 elif blo < bhi:
2025-07-02 05:51:15.367 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:15.380
2025-07-02 05:51:15.391 >       yield from g
2025-07-02 05:51:15.403
2025-07-02 05:51:15.415 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:15.424 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:15.431
2025-07-02 05:51:15.438 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:15.446 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:15.453 alo = 366, ahi = 1101
2025-07-02 05:51:15.463 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:15.476 blo = 366, bhi = 1101
2025-07-02 05:51:15.485
2025-07-02 05:51:15.496 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:15.505 r"""
2025-07-02 05:51:15.513 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:15.520 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:15.528 synch point, and intraline difference marking is done on the
2025-07-02 05:51:15.534 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:15.545
2025-07-02 05:51:15.554 Example:
2025-07-02 05:51:15.565
2025-07-02 05:51:15.581 >>> d = Differ()
2025-07-02 05:51:15.592 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:15.600 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:15.608 >>> print(''.join(results), end="")
2025-07-02 05:51:15.615 - abcDefghiJkl
2025-07-02 05:51:15.632 + abcdefGhijkl
2025-07-02 05:51:15.646 """
2025-07-02 05:51:15.657
2025-07-02 05:51:15.667 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:15.674 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:15.686 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:15.696 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:15.706 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:15.715
2025-07-02 05:51:15.724 # search for the pair that matches best without being identical
2025-07-02 05:51:15.737 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:15.749 # on junk -- unless we have to)
2025-07-02 05:51:15.762 for j in range(blo, bhi):
2025-07-02 05:51:15.772 bj = b[j]
2025-07-02 05:51:15.781 cruncher.set_seq2(bj)
2025-07-02 05:51:15.791 for i in range(alo, ahi):
2025-07-02 05:51:15.802 ai = a[i]
2025-07-02 05:51:15.812 if ai == bj:
2025-07-02 05:51:15.821 if eqi is None:
2025-07-02 05:51:15.831 eqi, eqj = i, j
2025-07-02 05:51:15.842 continue
2025-07-02 05:51:15.855 cruncher.set_seq1(ai)
2025-07-02 05:51:15.866 # computing similarity is expensive, so use the quick
2025-07-02 05:51:15.875 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:15.883 # compares by a factor of 3.
2025-07-02 05:51:15.894 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:15.902 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:15.911 # of the computation is cached by cruncher
2025-07-02 05:51:15.919 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:15.927 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:15.935 cruncher.ratio() > best_ratio:
2025-07-02 05:51:15.946 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:15.959 if best_ratio < cutoff:
2025-07-02 05:51:15.967 # no non-identical "pretty close" pair
2025-07-02 05:51:15.975 if eqi is None:
2025-07-02 05:51:15.986 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:15.997 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:16.007 return
2025-07-02 05:51:16.018 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:16.027 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:16.034 else:
2025-07-02 05:51:16.043 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:16.050 eqi = None
2025-07-02 05:51:16.059
2025-07-02 05:51:16.070 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:16.080 # identical
2025-07-02 05:51:16.087
2025-07-02 05:51:16.094 # pump out diffs from before the synch point
2025-07-02 05:51:16.103 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:16.113
2025-07-02 05:51:16.122 # do intraline marking on the synch pair
2025-07-02 05:51:16.131 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:16.138 if eqi is None:
2025-07-02 05:51:16.146 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:16.156 atags = btags = ""
2025-07-02 05:51:16.165 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:16.171 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:16.176 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:16.182 if tag == 'replace':
2025-07-02 05:51:16.187 atags += '^' * la
2025-07-02 05:51:16.194 btags += '^' * lb
2025-07-02 05:51:16.201 elif tag == 'delete':
2025-07-02 05:51:16.208 atags += '-' * la
2025-07-02 05:51:16.215 elif tag == 'insert':
2025-07-02 05:51:16.223 btags += '+' * lb
2025-07-02 05:51:16.230 elif tag == 'equal':
2025-07-02 05:51:16.238 atags += ' ' * la
2025-07-02 05:51:16.246 btags += ' ' * lb
2025-07-02 05:51:16.253 else:
2025-07-02 05:51:16.259 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:16.266 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:16.277 else:
2025-07-02 05:51:16.284 # the synch pair is identical
2025-07-02 05:51:16.292 yield '  ' + aelt
2025-07-02 05:51:16.298
2025-07-02 05:51:16.303 # pump out diffs from after the synch point
2025-07-02 05:51:16.308 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:16.312
2025-07-02 05:51:16.317 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:16.321 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:16.326
2025-07-02 05:51:16.331 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:16.336 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:16.340 alo = 367, ahi = 1101
2025-07-02 05:51:16.345 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:16.350 blo = 367, bhi = 1101
2025-07-02 05:51:16.355
2025-07-02 05:51:16.362 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:16.367 g = []
2025-07-02 05:51:16.371 if alo < ahi:
2025-07-02 05:51:16.376 if blo < bhi:
2025-07-02 05:51:16.381 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:16.386 else:
2025-07-02 05:51:16.391 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:16.402 elif blo < bhi:
2025-07-02 05:51:16.412 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:16.421
2025-07-02 05:51:16.429 >       yield from g
2025-07-02 05:51:16.436
2025-07-02 05:51:16.443 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:16.449 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:16.455
2025-07-02 05:51:16.461 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:16.467 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:16.472 alo = 367, ahi = 1101
2025-07-02 05:51:16.479 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:16.484 blo = 367, bhi = 1101
2025-07-02 05:51:16.491
2025-07-02 05:51:16.499 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:16.505 r"""
2025-07-02 05:51:16.511 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:16.519 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:16.526 synch point, and intraline difference marking is done on the
2025-07-02 05:51:16.533 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:16.539
2025-07-02 05:51:16.544 Example:
2025-07-02 05:51:16.549
2025-07-02 05:51:16.556 >>> d = Differ()
2025-07-02 05:51:16.562 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:16.569 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:16.582 >>> print(''.join(results), end="")
2025-07-02 05:51:16.589 - abcDefghiJkl
2025-07-02 05:51:16.602 + abcdefGhijkl
2025-07-02 05:51:16.622 """
2025-07-02 05:51:16.628
2025-07-02 05:51:16.636 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:16.645 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:16.651 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:16.659 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:16.670 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:16.680
2025-07-02 05:51:16.687 # search for the pair that matches best without being identical
2025-07-02 05:51:16.694 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:16.701 # on junk -- unless we have to)
2025-07-02 05:51:16.713 for j in range(blo, bhi):
2025-07-02 05:51:16.721 bj = b[j]
2025-07-02 05:51:16.729 cruncher.set_seq2(bj)
2025-07-02 05:51:16.735 for i in range(alo, ahi):
2025-07-02 05:51:16.742 ai = a[i]
2025-07-02 05:51:16.750 if ai == bj:
2025-07-02 05:51:16.758 if eqi is None:
2025-07-02 05:51:16.768 eqi, eqj = i, j
2025-07-02 05:51:16.776 continue
2025-07-02 05:51:16.782 cruncher.set_seq1(ai)
2025-07-02 05:51:16.792 # computing similarity is expensive, so use the quick
2025-07-02 05:51:16.805 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:16.814 # compares by a factor of 3.
2025-07-02 05:51:16.822 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:16.835 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:16.845 # of the computation is cached by cruncher
2025-07-02 05:51:16.852 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:16.859 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:16.865 cruncher.ratio() > best_ratio:
2025-07-02 05:51:16.871 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:16.886 if best_ratio < cutoff:
2025-07-02 05:51:16.896 # no non-identical "pretty close" pair
2025-07-02 05:51:16.902 if eqi is None:
2025-07-02 05:51:16.909 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:16.922 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:16.931 return
2025-07-02 05:51:16.939 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:16.946 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:16.952 else:
2025-07-02 05:51:16.958 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:16.964 eqi = None
2025-07-02 05:51:16.969
2025-07-02 05:51:16.974 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:16.985 # identical
2025-07-02 05:51:16.994
2025-07-02 05:51:17.005 # pump out diffs from before the synch point
2025-07-02 05:51:17.013 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:17.020
2025-07-02 05:51:17.027 # do intraline marking on the synch pair
2025-07-02 05:51:17.033 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:17.040 if eqi is None:
2025-07-02 05:51:17.048 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:17.055 atags = btags = ""
2025-07-02 05:51:17.063 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:17.071 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:17.079 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:17.086 if tag == 'replace':
2025-07-02 05:51:17.093 atags += '^' * la
2025-07-02 05:51:17.106 btags += '^' * lb
2025-07-02 05:51:17.117 elif tag == 'delete':
2025-07-02 05:51:17.129 atags += '-' * la
2025-07-02 05:51:17.139 elif tag == 'insert':
2025-07-02 05:51:17.150 btags += '+' * lb
2025-07-02 05:51:17.163 elif tag == 'equal':
2025-07-02 05:51:17.177 atags += ' ' * la
2025-07-02 05:51:17.188 btags += ' ' * lb
2025-07-02 05:51:17.196 else:
2025-07-02 05:51:17.204 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:17.211 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:17.219 else:
2025-07-02 05:51:17.230 # the synch pair is identical
2025-07-02 05:51:17.245 yield '  ' + aelt
2025-07-02 05:51:17.260
2025-07-02 05:51:17.272 # pump out diffs from after the synch point
2025-07-02 05:51:17.284 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:17.295
2025-07-02 05:51:17.306 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:17.317 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:17.325
2025-07-02 05:51:17.332 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:17.341 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:17.351 alo = 368, ahi = 1101
2025-07-02 05:51:17.359 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:17.365 blo = 368, bhi = 1101
2025-07-02 05:51:17.372
2025-07-02 05:51:17.378 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:17.391 g = []
2025-07-02 05:51:17.403 if alo < ahi:
2025-07-02 05:51:17.416 if blo < bhi:
2025-07-02 05:51:17.428 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:17.439 else:
2025-07-02 05:51:17.451 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:17.461 elif blo < bhi:
2025-07-02 05:51:17.471 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:17.478
2025-07-02 05:51:17.486 >       yield from g
2025-07-02 05:51:17.493
2025-07-02 05:51:17.501 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:17.507 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:17.515
2025-07-02 05:51:17.526 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:17.535 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:17.544 alo = 368, ahi = 1101
2025-07-02 05:51:17.557 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:17.569 blo = 368, bhi = 1101
2025-07-02 05:51:17.579
2025-07-02 05:51:17.590 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:17.599 r"""
2025-07-02 05:51:17.609 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:17.622 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:17.633 synch point, and intraline difference marking is done on the
2025-07-02 05:51:17.639 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:17.652
2025-07-02 05:51:17.663 Example:
2025-07-02 05:51:17.675
2025-07-02 05:51:17.685 >>> d = Differ()
2025-07-02 05:51:17.699 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:17.711 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:17.725 >>> print(''.join(results), end="")
2025-07-02 05:51:17.737 - abcDefghiJkl
2025-07-02 05:51:17.757 + abcdefGhijkl
2025-07-02 05:51:17.784 """
2025-07-02 05:51:17.796
2025-07-02 05:51:17.804 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:17.810 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:17.816 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:17.821 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:17.831 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:17.841
2025-07-02 05:51:17.849 # search for the pair that matches best without being identical
2025-07-02 05:51:17.859 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:17.869 # on junk -- unless we have to)
2025-07-02 05:51:17.878 for j in range(blo, bhi):
2025-07-02 05:51:17.886 bj = b[j]
2025-07-02 05:51:17.897 cruncher.set_seq2(bj)
2025-07-02 05:51:17.908 for i in range(alo, ahi):
2025-07-02 05:51:17.915 ai = a[i]
2025-07-02 05:51:17.922 if ai == bj:
2025-07-02 05:51:17.929 if eqi is None:
2025-07-02 05:51:17.935 eqi, eqj = i, j
2025-07-02 05:51:17.942 continue
2025-07-02 05:51:17.953 cruncher.set_seq1(ai)
2025-07-02 05:51:17.963 # computing similarity is expensive, so use the quick
2025-07-02 05:51:17.971 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:17.979 # compares by a factor of 3.
2025-07-02 05:51:17.991 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:18.003 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:18.011 # of the computation is cached by cruncher
2025-07-02 05:51:18.019 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:18.027 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:18.034 cruncher.ratio() > best_ratio:
2025-07-02 05:51:18.041 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:18.048 if best_ratio < cutoff:
2025-07-02 05:51:18.056 # no non-identical "pretty close" pair
2025-07-02 05:51:18.064 if eqi is None:
2025-07-02 05:51:18.072 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:18.079 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:18.086 return
2025-07-02 05:51:18.094 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:18.102 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:18.108 else:
2025-07-02 05:51:18.115 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:18.123 eqi = None
2025-07-02 05:51:18.131
2025-07-02 05:51:18.144 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:18.152 # identical
2025-07-02 05:51:18.159
2025-07-02 05:51:18.166 # pump out diffs from before the synch point
2025-07-02 05:51:18.171 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:18.176
2025-07-02 05:51:18.181 # do intraline marking on the synch pair
2025-07-02 05:51:18.187 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:18.192 if eqi is None:
2025-07-02 05:51:18.196 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:18.202 atags = btags = ""
2025-07-02 05:51:18.208 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:18.215 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:18.227 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:18.237 if tag == 'replace':
2025-07-02 05:51:18.250 atags += '^' * la
2025-07-02 05:51:18.261 btags += '^' * lb
2025-07-02 05:51:18.270 elif tag == 'delete':
2025-07-02 05:51:18.283 atags += '-' * la
2025-07-02 05:51:18.293 elif tag == 'insert':
2025-07-02 05:51:18.302 btags += '+' * lb
2025-07-02 05:51:18.309 elif tag == 'equal':
2025-07-02 05:51:18.315 atags += ' ' * la
2025-07-02 05:51:18.321 btags += ' ' * lb
2025-07-02 05:51:18.327 else:
2025-07-02 05:51:18.333 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:18.339 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:18.346 else:
2025-07-02 05:51:18.354 # the synch pair is identical
2025-07-02 05:51:18.365 yield '  ' + aelt
2025-07-02 05:51:18.374
2025-07-02 05:51:18.381 # pump out diffs from after the synch point
2025-07-02 05:51:18.387 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:18.394
2025-07-02 05:51:18.404 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:18.415 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:18.423
2025-07-02 05:51:18.430 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:18.437 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:18.443 alo = 369, ahi = 1101
2025-07-02 05:51:18.450 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:18.456 blo = 369, bhi = 1101
2025-07-02 05:51:18.463
2025-07-02 05:51:18.471 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:18.480 g = []
2025-07-02 05:51:18.491 if alo < ahi:
2025-07-02 05:51:18.503 if blo < bhi:
2025-07-02 05:51:18.512 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:18.519 else:
2025-07-02 05:51:18.525 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:18.530 elif blo < bhi:
2025-07-02 05:51:18.536 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:18.543
2025-07-02 05:51:18.551 >       yield from g
2025-07-02 05:51:18.560
2025-07-02 05:51:18.572 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:18.585 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:18.594
2025-07-02 05:51:18.601 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:18.609 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:18.616 alo = 369, ahi = 1101
2025-07-02 05:51:18.626 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:18.634 blo = 369, bhi = 1101
2025-07-02 05:51:18.641
2025-07-02 05:51:18.647 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:18.654 r"""
2025-07-02 05:51:18.661 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:18.667 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:18.674 synch point, and intraline difference marking is done on the
2025-07-02 05:51:18.680 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:18.685
2025-07-02 05:51:18.693 Example:
2025-07-02 05:51:18.700
2025-07-02 05:51:18.706 >>> d = Differ()
2025-07-02 05:51:18.714 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:18.723 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:18.729 >>> print(''.join(results), end="")
2025-07-02 05:51:18.736 - abcDefghiJkl
2025-07-02 05:51:18.753 + abcdefGhijkl
2025-07-02 05:51:18.774 """
2025-07-02 05:51:18.785
2025-07-02 05:51:18.797 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:18.808 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:18.819 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:18.828 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:18.837 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:18.843
2025-07-02 05:51:18.850 # search for the pair that matches best without being identical
2025-07-02 05:51:18.859 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:18.871 # on junk -- unless we have to)
2025-07-02 05:51:18.880 for j in range(blo, bhi):
2025-07-02 05:51:18.886 bj = b[j]
2025-07-02 05:51:18.898 cruncher.set_seq2(bj)
2025-07-02 05:51:18.908 for i in range(alo, ahi):
2025-07-02 05:51:18.915 ai = a[i]
2025-07-02 05:51:18.920 if ai == bj:
2025-07-02 05:51:18.926 if eqi is None:
2025-07-02 05:51:18.932 eqi, eqj = i, j
2025-07-02 05:51:18.939 continue
2025-07-02 05:51:18.946 cruncher.set_seq1(ai)
2025-07-02 05:51:18.957 # computing similarity is expensive, so use the quick
2025-07-02 05:51:18.966 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:18.973 # compares by a factor of 3.
2025-07-02 05:51:18.979 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:18.986 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:18.992 # of the computation is cached by cruncher
2025-07-02 05:51:18.998 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:19.006 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:19.014 cruncher.ratio() > best_ratio:
2025-07-02 05:51:19.019 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:19.024 if best_ratio < cutoff:
2025-07-02 05:51:19.030 # no non-identical "pretty close" pair
2025-07-02 05:51:19.041 if eqi is None:
2025-07-02 05:51:19.049 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:19.055 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:19.068 return
2025-07-02 05:51:19.079 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:19.086 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:19.093 else:
2025-07-02 05:51:19.101 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:19.108 eqi = None
2025-07-02 05:51:19.115
2025-07-02 05:51:19.122 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:19.133 # identical
2025-07-02 05:51:19.143
2025-07-02 05:51:19.151 # pump out diffs from before the synch point
2025-07-02 05:51:19.158 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:19.165
2025-07-02 05:51:19.172 # do intraline marking on the synch pair
2025-07-02 05:51:19.178 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:19.183 if eqi is None:
2025-07-02 05:51:19.188 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:19.195 atags = btags = ""
2025-07-02 05:51:19.202 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:19.209 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:19.215 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:19.222 if tag == 'replace':
2025-07-02 05:51:19.232 atags += '^' * la
2025-07-02 05:51:19.241 btags += '^' * lb
2025-07-02 05:51:19.247 elif tag == 'delete':
2025-07-02 05:51:19.254 atags += '-' * la
2025-07-02 05:51:19.262 elif tag == 'insert':
2025-07-02 05:51:19.269 btags += '+' * lb
2025-07-02 05:51:19.275 elif tag == 'equal':
2025-07-02 05:51:19.286 atags += ' ' * la
2025-07-02 05:51:19.297 btags += ' ' * lb
2025-07-02 05:51:19.305 else:
2025-07-02 05:51:19.321 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:19.332 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:19.342 else:
2025-07-02 05:51:19.351 # the synch pair is identical
2025-07-02 05:51:19.358 yield '  ' + aelt
2025-07-02 05:51:19.364
2025-07-02 05:51:19.370 # pump out diffs from after the synch point
2025-07-02 05:51:19.375 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:19.381
2025-07-02 05:51:19.387 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:19.394 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:19.403
2025-07-02 05:51:19.411 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:19.419 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:19.428 alo = 370, ahi = 1101
2025-07-02 05:51:19.439 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:19.448 blo = 370, bhi = 1101
2025-07-02 05:51:19.457
2025-07-02 05:51:19.464 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:19.470 g = []
2025-07-02 05:51:19.482 if alo < ahi:
2025-07-02 05:51:19.495 if blo < bhi:
2025-07-02 05:51:19.504 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:19.513 else:
2025-07-02 05:51:19.525 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:19.533 elif blo < bhi:
2025-07-02 05:51:19.539 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:19.544
2025-07-02 05:51:19.549 >       yield from g
2025-07-02 05:51:19.554
2025-07-02 05:51:19.559 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:19.564 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:19.569
2025-07-02 05:51:19.573 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:19.579 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:19.583 alo = 370, ahi = 1101
2025-07-02 05:51:19.588 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:19.593 blo = 370, bhi = 1101
2025-07-02 05:51:19.598
2025-07-02 05:51:19.605 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:19.611 r"""
2025-07-02 05:51:19.619 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:19.625 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:19.631 synch point, and intraline difference marking is done on the
2025-07-02 05:51:19.637 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:19.643
2025-07-02 05:51:19.651 Example:
2025-07-02 05:51:19.663
2025-07-02 05:51:19.672 >>> d = Differ()
2025-07-02 05:51:19.679 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:19.686 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:19.697 >>> print(''.join(results), end="")
2025-07-02 05:51:19.709 - abcDefghiJkl
2025-07-02 05:51:19.732 + abcdefGhijkl
2025-07-02 05:51:19.746 """
2025-07-02 05:51:19.752
2025-07-02 05:51:19.758 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:19.772 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:19.787 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:19.799 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:19.807 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:19.821
2025-07-02 05:51:19.834 # search for the pair that matches best without being identical
2025-07-02 05:51:19.845 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:19.853 # on junk -- unless we have to)
2025-07-02 05:51:19.861 for j in range(blo, bhi):
2025-07-02 05:51:19.872 bj = b[j]
2025-07-02 05:51:19.882 cruncher.set_seq2(bj)
2025-07-02 05:51:19.892 for i in range(alo, ahi):
2025-07-02 05:51:19.905 ai = a[i]
2025-07-02 05:51:19.917 if ai == bj:
2025-07-02 05:51:19.928 if eqi is None:
2025-07-02 05:51:19.940 eqi, eqj = i, j
2025-07-02 05:51:19.954 continue
2025-07-02 05:51:19.963 cruncher.set_seq1(ai)
2025-07-02 05:51:19.971 # computing similarity is expensive, so use the quick
2025-07-02 05:51:19.978 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:19.988 # compares by a factor of 3.
2025-07-02 05:51:19.999 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:20.007 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:20.014 # of the computation is cached by cruncher
2025-07-02 05:51:20.021 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:20.033 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:20.044 cruncher.ratio() > best_ratio:
2025-07-02 05:51:20.052 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:20.060 if best_ratio < cutoff:
2025-07-02 05:51:20.073 # no non-identical "pretty close" pair
2025-07-02 05:51:20.081 if eqi is None:
2025-07-02 05:51:20.089 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:20.096 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:20.103 return
2025-07-02 05:51:20.111 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:20.119 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:20.126 else:
2025-07-02 05:51:20.133 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:20.139 eqi = None
2025-07-02 05:51:20.153
2025-07-02 05:51:20.165 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:20.176 # identical
2025-07-02 05:51:20.185
2025-07-02 05:51:20.194 # pump out diffs from before the synch point
2025-07-02 05:51:20.202 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:20.209
2025-07-02 05:51:20.217 # do intraline marking on the synch pair
2025-07-02 05:51:20.225 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:20.232 if eqi is None:
2025-07-02 05:51:20.239 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:20.246 atags = btags = ""
2025-07-02 05:51:20.260 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:20.271 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:20.278 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:20.286 if tag == 'replace':
2025-07-02 05:51:20.294 atags += '^' * la
2025-07-02 05:51:20.301 btags += '^' * lb
2025-07-02 05:51:20.313 elif tag == 'delete':
2025-07-02 05:51:20.326 atags += '-' * la
2025-07-02 05:51:20.340 elif tag == 'insert':
2025-07-02 05:51:20.352 btags += '+' * lb
2025-07-02 05:51:20.364 elif tag == 'equal':
2025-07-02 05:51:20.375 atags += ' ' * la
2025-07-02 05:51:20.386 btags += ' ' * lb
2025-07-02 05:51:20.398 else:
2025-07-02 05:51:20.409 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:20.422 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:20.430 else:
2025-07-02 05:51:20.438 # the synch pair is identical
2025-07-02 05:51:20.444 yield '  ' + aelt
2025-07-02 05:51:20.456
2025-07-02 05:51:20.470 # pump out diffs from after the synch point
2025-07-02 05:51:20.483 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:20.495
2025-07-02 05:51:20.504 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:20.512 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:20.519
2025-07-02 05:51:20.533 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:20.543 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:20.549 alo = 371, ahi = 1101
2025-07-02 05:51:20.556 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:20.562 blo = 371, bhi = 1101
2025-07-02 05:51:20.568
2025-07-02 05:51:20.574 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:20.580 g = []
2025-07-02 05:51:20.586 if alo < ahi:
2025-07-02 05:51:20.592 if blo < bhi:
2025-07-02 05:51:20.599 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:20.607 else:
2025-07-02 05:51:20.617 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:20.631 elif blo < bhi:
2025-07-02 05:51:20.641 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:20.649
2025-07-02 05:51:20.655 >       yield from g
2025-07-02 05:51:20.665
2025-07-02 05:51:20.677 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:20.689 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:20.703
2025-07-02 05:51:20.713 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:20.726 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:20.736 alo = 371, ahi = 1101
2025-07-02 05:51:20.749 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:20.761 blo = 371, bhi = 1101
2025-07-02 05:51:20.771
2025-07-02 05:51:20.784 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:20.794 r"""
2025-07-02 05:51:20.804 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:20.815 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:20.825 synch point, and intraline difference marking is done on the
2025-07-02 05:51:20.833 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:20.841
2025-07-02 05:51:20.847 Example:
2025-07-02 05:51:20.852
2025-07-02 05:51:20.863 >>> d = Differ()
2025-07-02 05:51:20.874 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:20.884 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:20.891 >>> print(''.join(results), end="")
2025-07-02 05:51:20.898 - abcDefghiJkl
2025-07-02 05:51:20.917 + abcdefGhijkl
2025-07-02 05:51:20.940 """
2025-07-02 05:51:20.955
2025-07-02 05:51:20.968 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:20.977 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:20.989 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:21.000 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:21.009 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:21.017
2025-07-02 05:51:21.023 # search for the pair that matches best without being identical
2025-07-02 05:51:21.030 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:21.040 # on junk -- unless we have to)
2025-07-02 05:51:21.048 for j in range(blo, bhi):
2025-07-02 05:51:21.058 bj = b[j]
2025-07-02 05:51:21.069 cruncher.set_seq2(bj)
2025-07-02 05:51:21.077 for i in range(alo, ahi):
2025-07-02 05:51:21.085 ai = a[i]
2025-07-02 05:51:21.095 if ai == bj:
2025-07-02 05:51:21.106 if eqi is None:
2025-07-02 05:51:21.115 eqi, eqj = i, j
2025-07-02 05:51:21.124 continue
2025-07-02 05:51:21.131 cruncher.set_seq1(ai)
2025-07-02 05:51:21.138 # computing similarity is expensive, so use the quick
2025-07-02 05:51:21.145 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:21.152 # compares by a factor of 3.
2025-07-02 05:51:21.162 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:21.174 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:21.185 # of the computation is cached by cruncher
2025-07-02 05:51:21.194 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:21.201 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:21.208 cruncher.ratio() > best_ratio:
2025-07-02 05:51:21.216 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:21.223 if best_ratio < cutoff:
2025-07-02 05:51:21.230 # no non-identical "pretty close" pair
2025-07-02 05:51:21.237 if eqi is None:
2025-07-02 05:51:21.243 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:21.249 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:21.256 return
2025-07-02 05:51:21.264 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:21.270 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:21.278 else:
2025-07-02 05:51:21.287 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:21.294 eqi = None
2025-07-02 05:51:21.303
2025-07-02 05:51:21.313 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:21.320 # identical
2025-07-02 05:51:21.326
2025-07-02 05:51:21.333 # pump out diffs from before the synch point
2025-07-02 05:51:21.340 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:21.345
2025-07-02 05:51:21.351 # do intraline marking on the synch pair
2025-07-02 05:51:21.358 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:21.363 if eqi is None:
2025-07-02 05:51:21.372 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:21.382 atags = btags = ""
2025-07-02 05:51:21.391 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:21.397 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:21.404 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:21.410 if tag == 'replace':
2025-07-02 05:51:21.415 atags += '^' * la
2025-07-02 05:51:21.421 btags += '^' * lb
2025-07-02 05:51:21.427 elif tag == 'delete':
2025-07-02 05:51:21.433 atags += '-' * la
2025-07-02 05:51:21.443 elif tag == 'insert':
2025-07-02 05:51:21.456 btags += '+' * lb
2025-07-02 05:51:21.465 elif tag == 'equal':
2025-07-02 05:51:21.473 atags += ' ' * la
2025-07-02 05:51:21.479 btags += ' ' * lb
2025-07-02 05:51:21.487 else:
2025-07-02 05:51:21.499 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:21.507 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:21.517 else:
2025-07-02 05:51:21.526 # the synch pair is identical
2025-07-02 05:51:21.536 yield '  ' + aelt
2025-07-02 05:51:21.541
2025-07-02 05:51:21.551 # pump out diffs from after the synch point
2025-07-02 05:51:21.560 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:21.567
2025-07-02 05:51:21.574 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:21.583 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:21.595
2025-07-02 05:51:21.603 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:21.611 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:21.619 alo = 372, ahi = 1101
2025-07-02 05:51:21.633 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:21.641 blo = 372, bhi = 1101
2025-07-02 05:51:21.649
2025-07-02 05:51:21.655 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:21.663 g = []
2025-07-02 05:51:21.670 if alo < ahi:
2025-07-02 05:51:21.677 if blo < bhi:
2025-07-02 05:51:21.685 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:21.697 else:
2025-07-02 05:51:21.706 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:21.713 elif blo < bhi:
2025-07-02 05:51:21.720 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:21.726
2025-07-02 05:51:21.739 >       yield from g
2025-07-02 05:51:21.748
2025-07-02 05:51:21.755 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:21.764 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:21.777
2025-07-02 05:51:21.786 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:21.797 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:21.803 alo = 372, ahi = 1101
2025-07-02 05:51:21.811 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:21.818 blo = 372, bhi = 1101
2025-07-02 05:51:21.823
2025-07-02 05:51:21.828 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:21.835 r"""
2025-07-02 05:51:21.841 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:21.846 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:21.852 synch point, and intraline difference marking is done on the
2025-07-02 05:51:21.857 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:21.861
2025-07-02 05:51:21.869 Example:
2025-07-02 05:51:21.875
2025-07-02 05:51:21.880 >>> d = Differ()
2025-07-02 05:51:21.885 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:21.891 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:21.899 >>> print(''.join(results), end="")
2025-07-02 05:51:21.910 - abcDefghiJkl
2025-07-02 05:51:21.926 + abcdefGhijkl
2025-07-02 05:51:21.941 """
2025-07-02 05:51:21.947
2025-07-02 05:51:21.954 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:21.960 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:21.966 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:21.973 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:21.987 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:21.998
2025-07-02 05:51:22.008 # search for the pair that matches best without being identical
2025-07-02 05:51:22.022 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:22.031 # on junk -- unless we have to)
2025-07-02 05:51:22.039 for j in range(blo, bhi):
2025-07-02 05:51:22.046 bj = b[j]
2025-07-02 05:51:22.053 cruncher.set_seq2(bj)
2025-07-02 05:51:22.060 for i in range(alo, ahi):
2025-07-02 05:51:22.068 ai = a[i]
2025-07-02 05:51:22.075 if ai == bj:
2025-07-02 05:51:22.084 if eqi is None:
2025-07-02 05:51:22.096 eqi, eqj = i, j
2025-07-02 05:51:22.108 continue
2025-07-02 05:51:22.122 cruncher.set_seq1(ai)
2025-07-02 05:51:22.132 # computing similarity is expensive, so use the quick
2025-07-02 05:51:22.140 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:22.147 # compares by a factor of 3.
2025-07-02 05:51:22.154 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:22.162 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:22.171 # of the computation is cached by cruncher
2025-07-02 05:51:22.178 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:22.185 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:22.197 cruncher.ratio() > best_ratio:
2025-07-02 05:51:22.210 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:22.219 if best_ratio < cutoff:
2025-07-02 05:51:22.228 # no non-identical "pretty close" pair
2025-07-02 05:51:22.235 if eqi is None:
2025-07-02 05:51:22.241 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:22.247 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:22.253 return
2025-07-02 05:51:22.258 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:22.264 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:22.270 else:
2025-07-02 05:51:22.277 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:22.285 eqi = None
2025-07-02 05:51:22.292
2025-07-02 05:51:22.297 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:22.307 # identical
2025-07-02 05:51:22.316
2025-07-02 05:51:22.324 # pump out diffs from before the synch point
2025-07-02 05:51:22.331 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:22.338
2025-07-02 05:51:22.348 # do intraline marking on the synch pair
2025-07-02 05:51:22.360 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:22.369 if eqi is None:
2025-07-02 05:51:22.377 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:22.384 atags = btags = ""
2025-07-02 05:51:22.392 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:22.402 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:22.412 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:22.426 if tag == 'replace':
2025-07-02 05:51:22.435 atags += '^' * la
2025-07-02 05:51:22.445 btags += '^' * lb
2025-07-02 05:51:22.452 elif tag == 'delete':
2025-07-02 05:51:22.459 atags += '-' * la
2025-07-02 05:51:22.469 elif tag == 'insert':
2025-07-02 05:51:22.481 btags += '+' * lb
2025-07-02 05:51:22.492 elif tag == 'equal':
2025-07-02 05:51:22.502 atags += ' ' * la
2025-07-02 05:51:22.516 btags += ' ' * lb
2025-07-02 05:51:22.526 else:
2025-07-02 05:51:22.536 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:22.545 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:22.552 else:
2025-07-02 05:51:22.559 # the synch pair is identical
2025-07-02 05:51:22.565 yield '  ' + aelt
2025-07-02 05:51:22.571
2025-07-02 05:51:22.579 # pump out diffs from after the synch point
2025-07-02 05:51:22.585 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:22.591
2025-07-02 05:51:22.596 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:22.602 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:22.609
2025-07-02 05:51:22.615 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:22.621 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:22.626 alo = 373, ahi = 1101
2025-07-02 05:51:22.635 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:22.641 blo = 373, bhi = 1101
2025-07-02 05:51:22.650
2025-07-02 05:51:22.657 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:22.664 g = []
2025-07-02 05:51:22.672 if alo < ahi:
2025-07-02 05:51:22.678 if blo < bhi:
2025-07-02 05:51:22.685 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:22.690 else:
2025-07-02 05:51:22.696 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:22.702 elif blo < bhi:
2025-07-02 05:51:22.714 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:22.723
2025-07-02 05:51:22.734 >       yield from g
2025-07-02 05:51:22.743
2025-07-02 05:51:22.750 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:22.758 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:22.765
2025-07-02 05:51:22.772 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:22.785 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:22.798 alo = 373, ahi = 1101
2025-07-02 05:51:22.807 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:22.814 blo = 373, bhi = 1101
2025-07-02 05:51:22.821
2025-07-02 05:51:22.827 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:22.835 r"""
2025-07-02 05:51:22.842 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:22.852 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:22.863 synch point, and intraline difference marking is done on the
2025-07-02 05:51:22.871 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:22.877
2025-07-02 05:51:22.884 Example:
2025-07-02 05:51:22.891
2025-07-02 05:51:22.899 >>> d = Differ()
2025-07-02 05:51:22.906 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:22.915 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:22.926 >>> print(''.join(results), end="")
2025-07-02 05:51:22.939 - abcDefghiJkl
2025-07-02 05:51:22.957 + abcdefGhijkl
2025-07-02 05:51:22.971 """
2025-07-02 05:51:22.978
2025-07-02 05:51:22.987 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:22.995 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:23.002 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:23.011 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:23.018 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:23.028
2025-07-02 05:51:23.036 # search for the pair that matches best without being identical
2025-07-02 05:51:23.044 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:23.052 # on junk -- unless we have to)
2025-07-02 05:51:23.058 for j in range(blo, bhi):
2025-07-02 05:51:23.073 bj = b[j]
2025-07-02 05:51:23.087 cruncher.set_seq2(bj)
2025-07-02 05:51:23.098 for i in range(alo, ahi):
2025-07-02 05:51:23.110 ai = a[i]
2025-07-02 05:51:23.122 if ai == bj:
2025-07-02 05:51:23.130 if eqi is None:
2025-07-02 05:51:23.142 eqi, eqj = i, j
2025-07-02 05:51:23.152 continue
2025-07-02 05:51:23.164 cruncher.set_seq1(ai)
2025-07-02 05:51:23.171 # computing similarity is expensive, so use the quick
2025-07-02 05:51:23.179 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:23.186 # compares by a factor of 3.
2025-07-02 05:51:23.194 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:23.206 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:23.217 # of the computation is cached by cruncher
2025-07-02 05:51:23.228 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:23.239 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:23.251 cruncher.ratio() > best_ratio:
2025-07-02 05:51:23.261 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:23.272 if best_ratio < cutoff:
2025-07-02 05:51:23.278 # no non-identical "pretty close" pair
2025-07-02 05:51:23.291 if eqi is None:
2025-07-02 05:51:23.304 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:23.317 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:23.328 return
2025-07-02 05:51:23.336 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:23.343 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:23.352 else:
2025-07-02 05:51:23.362 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:23.376 eqi = None
2025-07-02 05:51:23.385
2025-07-02 05:51:23.395 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:23.407 # identical
2025-07-02 05:51:23.417
2025-07-02 05:51:23.429 # pump out diffs from before the synch point
2025-07-02 05:51:23.438 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:23.446
2025-07-02 05:51:23.456 # do intraline marking on the synch pair
2025-07-02 05:51:23.465 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:23.473 if eqi is None:
2025-07-02 05:51:23.480 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:23.487 atags = btags = ""
2025-07-02 05:51:23.495 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:23.506 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:23.514 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:23.522 if tag == 'replace':
2025-07-02 05:51:23.528 atags += '^' * la
2025-07-02 05:51:23.541 btags += '^' * lb
2025-07-02 05:51:23.552 elif tag == 'delete':
2025-07-02 05:51:23.565 atags += '-' * la
2025-07-02 05:51:23.576 elif tag == 'insert':
2025-07-02 05:51:23.584 btags += '+' * lb
2025-07-02 05:51:23.591 elif tag == 'equal':
2025-07-02 05:51:23.597 atags += ' ' * la
2025-07-02 05:51:23.603 btags += ' ' * lb
2025-07-02 05:51:23.611 else:
2025-07-02 05:51:23.621 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:23.630 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:23.641 else:
2025-07-02 05:51:23.653 # the synch pair is identical
2025-07-02 05:51:23.665 yield '  ' + aelt
2025-07-02 05:51:23.673
2025-07-02 05:51:23.681 # pump out diffs from after the synch point
2025-07-02 05:51:23.693 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:23.704
2025-07-02 05:51:23.714 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:23.724 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:23.732
2025-07-02 05:51:23.740 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:23.754 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:23.766 alo = 374, ahi = 1101
2025-07-02 05:51:23.778 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:23.787 blo = 374, bhi = 1101
2025-07-02 05:51:23.795
2025-07-02 05:51:23.803 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:23.814 g = []
2025-07-02 05:51:23.823 if alo < ahi:
2025-07-02 05:51:23.830 if blo < bhi:
2025-07-02 05:51:23.841 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:23.851 else:
2025-07-02 05:51:23.859 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:23.866 elif blo < bhi:
2025-07-02 05:51:23.877 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:23.887
2025-07-02 05:51:23.899 >       yield from g
2025-07-02 05:51:23.908
2025-07-02 05:51:23.917 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:23.924 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:23.937
2025-07-02 05:51:23.947 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:23.956 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:23.962 alo = 374, ahi = 1101
2025-07-02 05:51:23.967 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:23.978 blo = 374, bhi = 1101
2025-07-02 05:51:23.989
2025-07-02 05:51:23.997 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:24.005 r"""
2025-07-02 05:51:24.011 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:24.019 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:24.030 synch point, and intraline difference marking is done on the
2025-07-02 05:51:24.041 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:24.047
2025-07-02 05:51:24.054 Example:
2025-07-02 05:51:24.066
2025-07-02 05:51:24.075 >>> d = Differ()
2025-07-02 05:51:24.084 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:24.091 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:24.099 >>> print(''.join(results), end="")
2025-07-02 05:51:24.108 - abcDefghiJkl
2025-07-02 05:51:24.123 + abcdefGhijkl
2025-07-02 05:51:24.143 """
2025-07-02 05:51:24.152
2025-07-02 05:51:24.165 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:24.171 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:24.178 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:24.184 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:24.190 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:24.197
2025-07-02 05:51:24.205 # search for the pair that matches best without being identical
2025-07-02 05:51:24.212 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:24.220 # on junk -- unless we have to)
2025-07-02 05:51:24.227 for j in range(blo, bhi):
2025-07-02 05:51:24.235 bj = b[j]
2025-07-02 05:51:24.243 cruncher.set_seq2(bj)
2025-07-02 05:51:24.251 for i in range(alo, ahi):
2025-07-02 05:51:24.260 ai = a[i]
2025-07-02 05:51:24.272 if ai == bj:
2025-07-02 05:51:24.282 if eqi is None:
2025-07-02 05:51:24.290 eqi, eqj = i, j
2025-07-02 05:51:24.296 continue
2025-07-02 05:51:24.302 cruncher.set_seq1(ai)
2025-07-02 05:51:24.309 # computing similarity is expensive, so use the quick
2025-07-02 05:51:24.315 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:24.322 # compares by a factor of 3.
2025-07-02 05:51:24.331 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:24.339 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:24.347 # of the computation is cached by cruncher
2025-07-02 05:51:24.355 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:24.363 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:24.371 cruncher.ratio() > best_ratio:
2025-07-02 05:51:24.380 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:24.389 if best_ratio < cutoff:
2025-07-02 05:51:24.396 # no non-identical "pretty close" pair
2025-07-02 05:51:24.404 if eqi is None:
2025-07-02 05:51:24.413 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:24.424 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:24.435 return
2025-07-02 05:51:24.447 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:24.460 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:24.468 else:
2025-07-02 05:51:24.475 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:24.482 eqi = None
2025-07-02 05:51:24.493
2025-07-02 05:51:24.506 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:24.518 # identical
2025-07-02 05:51:24.531
2025-07-02 05:51:24.543 # pump out diffs from before the synch point
2025-07-02 05:51:24.555 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:24.562
2025-07-02 05:51:24.573 # do intraline marking on the synch pair
2025-07-02 05:51:24.583 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:24.592 if eqi is None:
2025-07-02 05:51:24.600 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:24.606 atags = btags = ""
2025-07-02 05:51:24.612 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:24.619 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:24.632 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:24.641 if tag == 'replace':
2025-07-02 05:51:24.654 atags += '^' * la
2025-07-02 05:51:24.665 btags += '^' * lb
2025-07-02 05:51:24.676 elif tag == 'delete':
2025-07-02 05:51:24.690 atags += '-' * la
2025-07-02 05:51:24.701 elif tag == 'insert':
2025-07-02 05:51:24.709 btags += '+' * lb
2025-07-02 05:51:24.716 elif tag == 'equal':
2025-07-02 05:51:24.724 atags += ' ' * la
2025-07-02 05:51:24.732 btags += ' ' * lb
2025-07-02 05:51:24.739 else:
2025-07-02 05:51:24.747 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:24.755 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:24.762 else:
2025-07-02 05:51:24.770 # the synch pair is identical
2025-07-02 05:51:24.776 yield '  ' + aelt
2025-07-02 05:51:24.786
2025-07-02 05:51:24.794 # pump out diffs from after the synch point
2025-07-02 05:51:24.806 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:24.815
2025-07-02 05:51:24.824 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:24.833 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:24.839
2025-07-02 05:51:24.847 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:24.861 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:24.874 alo = 375, ahi = 1101
2025-07-02 05:51:24.886 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:24.894 blo = 375, bhi = 1101
2025-07-02 05:51:24.900
2025-07-02 05:51:24.907 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:24.914 g = []
2025-07-02 05:51:24.919 if alo < ahi:
2025-07-02 05:51:24.926 if blo < bhi:
2025-07-02 05:51:24.937 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:24.947 else:
2025-07-02 05:51:24.960 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:24.971 elif blo < bhi:
2025-07-02 05:51:24.981 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:24.988
2025-07-02 05:51:25.002 >       yield from g
2025-07-02 05:51:25.016
2025-07-02 05:51:25.025 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:25.037 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:25.050
2025-07-02 05:51:25.060 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:25.074 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:25.085 alo = 375, ahi = 1101
2025-07-02 05:51:25.102 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:25.113 blo = 375, bhi = 1101
2025-07-02 05:51:25.124
2025-07-02 05:51:25.132 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:25.138 r"""
2025-07-02 05:51:25.144 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:25.150 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:25.155 synch point, and intraline difference marking is done on the
2025-07-02 05:51:25.161 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:25.166
2025-07-02 05:51:25.172 Example:
2025-07-02 05:51:25.178
2025-07-02 05:51:25.184 >>> d = Differ()
2025-07-02 05:51:25.190 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:25.196 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:25.206 >>> print(''.join(results), end="")
2025-07-02 05:51:25.215 - abcDefghiJkl
2025-07-02 05:51:25.230 + abcdefGhijkl
2025-07-02 05:51:25.252 """
2025-07-02 05:51:25.263
2025-07-02 05:51:25.271 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:25.279 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:25.287 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:25.294 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:25.301 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:25.309
2025-07-02 05:51:25.316 # search for the pair that matches best without being identical
2025-07-02 05:51:25.324 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:25.331 # on junk -- unless we have to)
2025-07-02 05:51:25.338 for j in range(blo, bhi):
2025-07-02 05:51:25.346 bj = b[j]
2025-07-02 05:51:25.353 cruncher.set_seq2(bj)
2025-07-02 05:51:25.360 for i in range(alo, ahi):
2025-07-02 05:51:25.367 ai = a[i]
2025-07-02 05:51:25.375 if ai == bj:
2025-07-02 05:51:25.382 if eqi is None:
2025-07-02 05:51:25.388 eqi, eqj = i, j
2025-07-02 05:51:25.395 continue
2025-07-02 05:51:25.403 cruncher.set_seq1(ai)
2025-07-02 05:51:25.416 # computing similarity is expensive, so use the quick
2025-07-02 05:51:25.426 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:25.435 # compares by a factor of 3.
2025-07-02 05:51:25.449 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:25.457 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:25.465 # of the computation is cached by cruncher
2025-07-02 05:51:25.471 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:25.480 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:25.492 cruncher.ratio() > best_ratio:
2025-07-02 05:51:25.502 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:25.509 if best_ratio < cutoff:
2025-07-02 05:51:25.521 # no non-identical "pretty close" pair
2025-07-02 05:51:25.534 if eqi is None:
2025-07-02 05:51:25.545 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:25.555 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:25.565 return
2025-07-02 05:51:25.575 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:25.586 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:25.595 else:
2025-07-02 05:51:25.602 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:25.611 eqi = None
2025-07-02 05:51:25.624
2025-07-02 05:51:25.633 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:25.641 # identical
2025-07-02 05:51:25.647
2025-07-02 05:51:25.654 # pump out diffs from before the synch point
2025-07-02 05:51:25.660 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:25.666
2025-07-02 05:51:25.672 # do intraline marking on the synch pair
2025-07-02 05:51:25.679 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:25.687 if eqi is None:
2025-07-02 05:51:25.699 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:25.707 atags = btags = ""
2025-07-02 05:51:25.714 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:25.725 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:25.734 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:25.744 if tag == 'replace':
2025-07-02 05:51:25.752 atags += '^' * la
2025-07-02 05:51:25.759 btags += '^' * lb
2025-07-02 05:51:25.765 elif tag == 'delete':
2025-07-02 05:51:25.771 atags += '-' * la
2025-07-02 05:51:25.777 elif tag == 'insert':
2025-07-02 05:51:25.784 btags += '+' * lb
2025-07-02 05:51:25.790 elif tag == 'equal':
2025-07-02 05:51:25.795 atags += ' ' * la
2025-07-02 05:51:25.800 btags += ' ' * lb
2025-07-02 05:51:25.807 else:
2025-07-02 05:51:25.819 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:25.829 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:25.839 else:
2025-07-02 05:51:25.853 # the synch pair is identical
2025-07-02 05:51:25.867 yield '  ' + aelt
2025-07-02 05:51:25.877
2025-07-02 05:51:25.890 # pump out diffs from after the synch point
2025-07-02 05:51:25.902 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:25.912
2025-07-02 05:51:25.920 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:25.927 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:25.940
2025-07-02 05:51:25.952 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:25.964 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:25.973 alo = 378, ahi = 1101
2025-07-02 05:51:25.987 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:26.001 blo = 378, bhi = 1101
2025-07-02 05:51:26.010
2025-07-02 05:51:26.020 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:26.031 g = []
2025-07-02 05:51:26.042 if alo < ahi:
2025-07-02 05:51:26.055 if blo < bhi:
2025-07-02 05:51:26.066 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:26.076 else:
2025-07-02 05:51:26.084 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:26.091 elif blo < bhi:
2025-07-02 05:51:26.102 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:26.112
2025-07-02 05:51:26.118 >       yield from g
2025-07-02 05:51:26.124
2025-07-02 05:51:26.129 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:26.133 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:26.138
2025-07-02 05:51:26.144 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:26.149 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:26.154 alo = 378, ahi = 1101
2025-07-02 05:51:26.159 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:26.163 blo = 378, bhi = 1101
2025-07-02 05:51:26.168
2025-07-02 05:51:26.173 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:26.178 r"""
2025-07-02 05:51:26.182 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:26.188 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:26.194 synch point, and intraline difference marking is done on the
2025-07-02 05:51:26.198 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:26.206
2025-07-02 05:51:26.214 Example:
2025-07-02 05:51:26.222
2025-07-02 05:51:26.229 >>> d = Differ()
2025-07-02 05:51:26.235 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:26.242 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:26.254 >>> print(''.join(results), end="")
2025-07-02 05:51:26.266 - abcDefghiJkl
2025-07-02 05:51:26.283 + abcdefGhijkl
2025-07-02 05:51:26.307 """
2025-07-02 05:51:26.318
2025-07-02 05:51:26.327 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:26.335 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:26.343 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:26.355 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:26.365 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:26.373
2025-07-02 05:51:26.380 # search for the pair that matches best without being identical
2025-07-02 05:51:26.387 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:26.393 # on junk -- unless we have to)
2025-07-02 05:51:26.400 for j in range(blo, bhi):
2025-07-02 05:51:26.406 bj = b[j]
2025-07-02 05:51:26.417 cruncher.set_seq2(bj)
2025-07-02 05:51:26.428 for i in range(alo, ahi):
2025-07-02 05:51:26.436 ai = a[i]
2025-07-02 05:51:26.443 if ai == bj:
2025-07-02 05:51:26.450 if eqi is None:
2025-07-02 05:51:26.462 eqi, eqj = i, j
2025-07-02 05:51:26.473 continue
2025-07-02 05:51:26.485 cruncher.set_seq1(ai)
2025-07-02 05:51:26.497 # computing similarity is expensive, so use the quick
2025-07-02 05:51:26.507 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:26.515 # compares by a factor of 3.
2025-07-02 05:51:26.522 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:26.531 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:26.546 # of the computation is cached by cruncher
2025-07-02 05:51:26.559 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:26.569 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:26.576 cruncher.ratio() > best_ratio:
2025-07-02 05:51:26.581 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:26.586 if best_ratio < cutoff:
2025-07-02 05:51:26.590 # no non-identical "pretty close" pair
2025-07-02 05:51:26.600 if eqi is None:
2025-07-02 05:51:26.610 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:26.616 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:26.628 return
2025-07-02 05:51:26.642 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:26.654 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:26.661 else:
2025-07-02 05:51:26.670 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:26.679 eqi = None
2025-07-02 05:51:26.687
2025-07-02 05:51:26.698 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:26.710 # identical
2025-07-02 05:51:26.717
2025-07-02 05:51:26.724 # pump out diffs from before the synch point
2025-07-02 05:51:26.733 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:26.741
2025-07-02 05:51:26.748 # do intraline marking on the synch pair
2025-07-02 05:51:26.755 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:26.762 if eqi is None:
2025-07-02 05:51:26.771 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:26.786 atags = btags = ""
2025-07-02 05:51:26.798 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:26.812 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:26.823 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:26.836 if tag == 'replace':
2025-07-02 05:51:26.846 atags += '^' * la
2025-07-02 05:51:26.859 btags += '^' * lb
2025-07-02 05:51:26.871 elif tag == 'delete':
2025-07-02 05:51:26.880 atags += '-' * la
2025-07-02 05:51:26.887 elif tag == 'insert':
2025-07-02 05:51:26.897 btags += '+' * lb
2025-07-02 05:51:26.910 elif tag == 'equal':
2025-07-02 05:51:26.920 atags += ' ' * la
2025-07-02 05:51:26.928 btags += ' ' * lb
2025-07-02 05:51:26.935 else:
2025-07-02 05:51:26.942 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:26.952 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:26.962 else:
2025-07-02 05:51:26.972 # the synch pair is identical
2025-07-02 05:51:26.979 yield '  ' + aelt
2025-07-02 05:51:26.986
2025-07-02 05:51:26.997 # pump out diffs from after the synch point
2025-07-02 05:51:27.008 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:27.014
2025-07-02 05:51:27.021 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:27.025 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:27.030
2025-07-02 05:51:27.035 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:27.042 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:27.048 alo = 379, ahi = 1101
2025-07-02 05:51:27.056 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:27.062 blo = 379, bhi = 1101
2025-07-02 05:51:27.074
2025-07-02 05:51:27.080 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:27.087 g = []
2025-07-02 05:51:27.103 if alo < ahi:
2025-07-02 05:51:27.114 if blo < bhi:
2025-07-02 05:51:27.126 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:27.137 else:
2025-07-02 05:51:27.145 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:27.153 elif blo < bhi:
2025-07-02 05:51:27.158 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:27.163
2025-07-02 05:51:27.168 >       yield from g
2025-07-02 05:51:27.173
2025-07-02 05:51:27.178 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:27.187 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:27.198
2025-07-02 05:51:27.209 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:27.220 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:27.231 alo = 379, ahi = 1101
2025-07-02 05:51:27.243 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:27.254 blo = 379, bhi = 1101
2025-07-02 05:51:27.263
2025-07-02 05:51:27.276 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:27.286 r"""
2025-07-02 05:51:27.295 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:27.303 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:27.310 synch point, and intraline difference marking is done on the
2025-07-02 05:51:27.321 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:27.331
2025-07-02 05:51:27.339 Example:
2025-07-02 05:51:27.346
2025-07-02 05:51:27.356 >>> d = Differ()
2025-07-02 05:51:27.369 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:27.381 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:27.393 >>> print(''.join(results), end="")
2025-07-02 05:51:27.404 - abcDefghiJkl
2025-07-02 05:51:27.427 + abcdefGhijkl
2025-07-02 05:51:27.449 """
2025-07-02 05:51:27.458
2025-07-02 05:51:27.469 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:27.478 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:27.486 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:27.493 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:27.506 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:27.518
2025-07-02 05:51:27.531 # search for the pair that matches best without being identical
2025-07-02 05:51:27.544 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:27.554 # on junk -- unless we have to)
2025-07-02 05:51:27.567 for j in range(blo, bhi):
2025-07-02 05:51:27.578 bj = b[j]
2025-07-02 05:51:27.587 cruncher.set_seq2(bj)
2025-07-02 05:51:27.599 for i in range(alo, ahi):
2025-07-02 05:51:27.607 ai = a[i]
2025-07-02 05:51:27.615 if ai == bj:
2025-07-02 05:51:27.622 if eqi is None:
2025-07-02 05:51:27.629 eqi, eqj = i, j
2025-07-02 05:51:27.634 continue
2025-07-02 05:51:27.639 cruncher.set_seq1(ai)
2025-07-02 05:51:27.646 # computing similarity is expensive, so use the quick
2025-07-02 05:51:27.652 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:27.659 # compares by a factor of 3.
2025-07-02 05:51:27.671 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:27.682 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:27.691 # of the computation is cached by cruncher
2025-07-02 05:51:27.700 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:27.708 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:27.714 cruncher.ratio() > best_ratio:
2025-07-02 05:51:27.721 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:27.727 if best_ratio < cutoff:
2025-07-02 05:51:27.733 # no non-identical "pretty close" pair
2025-07-02 05:51:27.739 if eqi is None:
2025-07-02 05:51:27.748 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:27.758 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:27.772 return
2025-07-02 05:51:27.783 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:27.798 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:27.807 else:
2025-07-02 05:51:27.814 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:27.821 eqi = None
2025-07-02 05:51:27.827
2025-07-02 05:51:27.837 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:27.850 # identical
2025-07-02 05:51:27.863
2025-07-02 05:51:27.874 # pump out diffs from before the synch point
2025-07-02 05:51:27.881 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:27.886
2025-07-02 05:51:27.891 # do intraline marking on the synch pair
2025-07-02 05:51:27.897 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:27.903 if eqi is None:
2025-07-02 05:51:27.908 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:27.912 atags = btags = ""
2025-07-02 05:51:27.918 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:27.923 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:27.929 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:27.935 if tag == 'replace':
2025-07-02 05:51:27.942 atags += '^' * la
2025-07-02 05:51:27.957 btags += '^' * lb
2025-07-02 05:51:27.966 elif tag == 'delete':
2025-07-02 05:51:27.974 atags += '-' * la
2025-07-02 05:51:27.981 elif tag == 'insert':
2025-07-02 05:51:27.987 btags += '+' * lb
2025-07-02 05:51:27.993 elif tag == 'equal':
2025-07-02 05:51:27.999 atags += ' ' * la
2025-07-02 05:51:28.006 btags += ' ' * lb
2025-07-02 05:51:28.015 else:
2025-07-02 05:51:28.023 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:28.030 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:28.038 else:
2025-07-02 05:51:28.049 # the synch pair is identical
2025-07-02 05:51:28.061 yield '  ' + aelt
2025-07-02 05:51:28.074
2025-07-02 05:51:28.083 # pump out diffs from after the synch point
2025-07-02 05:51:28.092 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:28.099
2025-07-02 05:51:28.108 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:28.116 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:28.123
2025-07-02 05:51:28.129 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:28.136 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:28.143 alo = 380, ahi = 1101
2025-07-02 05:51:28.151 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:28.159 blo = 380, bhi = 1101
2025-07-02 05:51:28.167
2025-07-02 05:51:28.180 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:28.192 g = []
2025-07-02 05:51:28.200 if alo < ahi:
2025-07-02 05:51:28.208 if blo < bhi:
2025-07-02 05:51:28.214 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:28.223 else:
2025-07-02 05:51:28.233 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:28.242 elif blo < bhi:
2025-07-02 05:51:28.251 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:28.258
2025-07-02 05:51:28.270 >       yield from g
2025-07-02 05:51:28.280
2025-07-02 05:51:28.288 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:28.295 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:28.301
2025-07-02 05:51:28.306 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:28.314 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:28.320 alo = 380, ahi = 1101
2025-07-02 05:51:28.334 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:28.343 blo = 380, bhi = 1101
2025-07-02 05:51:28.350
2025-07-02 05:51:28.356 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:28.361 r"""
2025-07-02 05:51:28.366 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:28.370 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:28.375 synch point, and intraline difference marking is done on the
2025-07-02 05:51:28.380 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:28.385
2025-07-02 05:51:28.390 Example:
2025-07-02 05:51:28.395
2025-07-02 05:51:28.400 >>> d = Differ()
2025-07-02 05:51:28.405 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:28.412 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:28.420 >>> print(''.join(results), end="")
2025-07-02 05:51:28.432 - abcDefghiJkl
2025-07-02 05:51:28.451 + abcdefGhijkl
2025-07-02 05:51:28.468 """
2025-07-02 05:51:28.477
2025-07-02 05:51:28.490 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:28.503 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:28.510 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:28.523 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:28.531 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:28.539
2025-07-02 05:51:28.546 # search for the pair that matches best without being identical
2025-07-02 05:51:28.558 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:28.568 # on junk -- unless we have to)
2025-07-02 05:51:28.577 for j in range(blo, bhi):
2025-07-02 05:51:28.589 bj = b[j]
2025-07-02 05:51:28.598 cruncher.set_seq2(bj)
2025-07-02 05:51:28.610 for i in range(alo, ahi):
2025-07-02 05:51:28.619 ai = a[i]
2025-07-02 05:51:28.628 if ai == bj:
2025-07-02 05:51:28.635 if eqi is None:
2025-07-02 05:51:28.642 eqi, eqj = i, j
2025-07-02 05:51:28.654 continue
2025-07-02 05:51:28.665 cruncher.set_seq1(ai)
2025-07-02 05:51:28.680 # computing similarity is expensive, so use the quick
2025-07-02 05:51:28.691 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:28.701 # compares by a factor of 3.
2025-07-02 05:51:28.712 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:28.722 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:28.729 # of the computation is cached by cruncher
2025-07-02 05:51:28.737 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:28.743 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:28.756 cruncher.ratio() > best_ratio:
2025-07-02 05:51:28.768 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:28.781 if best_ratio < cutoff:
2025-07-02 05:51:28.791 # no non-identical "pretty close" pair
2025-07-02 05:51:28.803 if eqi is None:
2025-07-02 05:51:28.814 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:28.825 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:28.837 return
2025-07-02 05:51:28.850 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:28.859 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:28.867 else:
2025-07-02 05:51:28.875 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:28.882 eqi = None
2025-07-02 05:51:28.891
2025-07-02 05:51:28.907 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:28.918 # identical
2025-07-02 05:51:28.928
2025-07-02 05:51:28.939 # pump out diffs from before the synch point
2025-07-02 05:51:28.949 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:28.957
2025-07-02 05:51:28.964 # do intraline marking on the synch pair
2025-07-02 05:51:28.970 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:28.976 if eqi is None:
2025-07-02 05:51:28.983 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:28.990 atags = btags = ""
2025-07-02 05:51:28.996 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:29.009 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:29.021 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:29.030 if tag == 'replace':
2025-07-02 05:51:29.039 atags += '^' * la
2025-07-02 05:51:29.049 btags += '^' * lb
2025-07-02 05:51:29.056 elif tag == 'delete':
2025-07-02 05:51:29.063 atags += '-' * la
2025-07-02 05:51:29.072 elif tag == 'insert':
2025-07-02 05:51:29.085 btags += '+' * lb
2025-07-02 05:51:29.098 elif tag == 'equal':
2025-07-02 05:51:29.108 atags += ' ' * la
2025-07-02 05:51:29.122 btags += ' ' * lb
2025-07-02 05:51:29.136 else:
2025-07-02 05:51:29.148 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:29.156 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:29.163 else:
2025-07-02 05:51:29.168 # the synch pair is identical
2025-07-02 05:51:29.173 yield '  ' + aelt
2025-07-02 05:51:29.179
2025-07-02 05:51:29.186 # pump out diffs from after the synch point
2025-07-02 05:51:29.192 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:29.200
2025-07-02 05:51:29.212 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:29.220 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:29.227
2025-07-02 05:51:29.233 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:29.242 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:29.252 alo = 381, ahi = 1101
2025-07-02 05:51:29.262 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:29.271 blo = 381, bhi = 1101
2025-07-02 05:51:29.283
2025-07-02 05:51:29.296 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:29.305 g = []
2025-07-02 05:51:29.312 if alo < ahi:
2025-07-02 05:51:29.320 if blo < bhi:
2025-07-02 05:51:29.328 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:29.340 else:
2025-07-02 05:51:29.349 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:29.358 elif blo < bhi:
2025-07-02 05:51:29.366 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:29.374
2025-07-02 05:51:29.385 >       yield from g
2025-07-02 05:51:29.395
2025-07-02 05:51:29.402 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:29.408 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:29.413
2025-07-02 05:51:29.423 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:29.432 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:29.438 alo = 381, ahi = 1101
2025-07-02 05:51:29.445 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:29.451 blo = 381, bhi = 1101
2025-07-02 05:51:29.456
2025-07-02 05:51:29.462 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:29.470 r"""
2025-07-02 05:51:29.477 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:29.483 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:29.490 synch point, and intraline difference marking is done on the
2025-07-02 05:51:29.503 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:29.514
2025-07-02 05:51:29.523 Example:
2025-07-02 05:51:29.531
2025-07-02 05:51:29.537 >>> d = Differ()
2025-07-02 05:51:29.543 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:29.548 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:29.553 >>> print(''.join(results), end="")
2025-07-02 05:51:29.559 - abcDefghiJkl
2025-07-02 05:51:29.576 + abcdefGhijkl
2025-07-02 05:51:29.600 """
2025-07-02 05:51:29.613
2025-07-02 05:51:29.626 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:29.638 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:29.648 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:29.656 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:29.663 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:29.669
2025-07-02 05:51:29.677 # search for the pair that matches best without being identical
2025-07-02 05:51:29.685 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:29.692 # on junk -- unless we have to)
2025-07-02 05:51:29.699 for j in range(blo, bhi):
2025-07-02 05:51:29.705 bj = b[j]
2025-07-02 05:51:29.712 cruncher.set_seq2(bj)
2025-07-02 05:51:29.721 for i in range(alo, ahi):
2025-07-02 05:51:29.732 ai = a[i]
2025-07-02 05:51:29.745 if ai == bj:
2025-07-02 05:51:29.754 if eqi is None:
2025-07-02 05:51:29.763 eqi, eqj = i, j
2025-07-02 05:51:29.771 continue
2025-07-02 05:51:29.777 cruncher.set_seq1(ai)
2025-07-02 05:51:29.784 # computing similarity is expensive, so use the quick
2025-07-02 05:51:29.792 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:29.801 # compares by a factor of 3.
2025-07-02 05:51:29.809 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:29.816 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:29.822 # of the computation is cached by cruncher
2025-07-02 05:51:29.829 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:29.836 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:29.843 cruncher.ratio() > best_ratio:
2025-07-02 05:51:29.851 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:29.859 if best_ratio < cutoff:
2025-07-02 05:51:29.868 # no non-identical "pretty close" pair
2025-07-02 05:51:29.875 if eqi is None:
2025-07-02 05:51:29.886 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:29.895 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:29.903 return
2025-07-02 05:51:29.910 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:29.917 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:29.924 else:
2025-07-02 05:51:29.930 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:29.941 eqi = None
2025-07-02 05:51:29.951
2025-07-02 05:51:29.960 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:29.967 # identical
2025-07-02 05:51:29.975
2025-07-02 05:51:29.983 # pump out diffs from before the synch point
2025-07-02 05:51:29.990 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:29.997
2025-07-02 05:51:30.003 # do intraline marking on the synch pair
2025-07-02 05:51:30.011 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:30.024 if eqi is None:
2025-07-02 05:51:30.036 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:30.044 atags = btags = ""
2025-07-02 05:51:30.052 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:30.062 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:30.074 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:30.083 if tag == 'replace':
2025-07-02 05:51:30.096 atags += '^' * la
2025-07-02 05:51:30.106 btags += '^' * lb
2025-07-02 05:51:30.115 elif tag == 'delete':
2025-07-02 05:51:30.122 atags += '-' * la
2025-07-02 05:51:30.128 elif tag == 'insert':
2025-07-02 05:51:30.134 btags += '+' * lb
2025-07-02 05:51:30.139 elif tag == 'equal':
2025-07-02 05:51:30.144 atags += ' ' * la
2025-07-02 05:51:30.149 btags += ' ' * lb
2025-07-02 05:51:30.154 else:
2025-07-02 05:51:30.162 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:30.168 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:30.175 else:
2025-07-02 05:51:30.183 # the synch pair is identical
2025-07-02 05:51:30.194 yield '  ' + aelt
2025-07-02 05:51:30.205
2025-07-02 05:51:30.216 # pump out diffs from after the synch point
2025-07-02 05:51:30.227 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:30.238
2025-07-02 05:51:30.250 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:30.263 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:30.273
2025-07-02 05:51:30.281 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:30.289 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:30.296 alo = 382, ahi = 1101
2025-07-02 05:51:30.302 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:30.311 blo = 382, bhi = 1101
2025-07-02 05:51:30.319
2025-07-02 05:51:30.326 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:30.332 g = []
2025-07-02 05:51:30.338 if alo < ahi:
2025-07-02 05:51:30.347 if blo < bhi:
2025-07-02 05:51:30.354 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:30.363 else:
2025-07-02 05:51:30.373 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:30.382 elif blo < bhi:
2025-07-02 05:51:30.391 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:30.402
2025-07-02 05:51:30.413 >       yield from g
2025-07-02 05:51:30.420
2025-07-02 05:51:30.428 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:30.435 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:30.441
2025-07-02 05:51:30.446 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:30.452 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:30.458 alo = 382, ahi = 1101
2025-07-02 05:51:30.465 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:30.473 blo = 382, bhi = 1101
2025-07-02 05:51:30.482
2025-07-02 05:51:30.493 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:30.501 r"""
2025-07-02 05:51:30.508 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:30.514 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:30.523 synch point, and intraline difference marking is done on the
2025-07-02 05:51:30.533 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:30.547
2025-07-02 05:51:30.557 Example:
2025-07-02 05:51:30.570
2025-07-02 05:51:30.577 >>> d = Differ()
2025-07-02 05:51:30.582 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:30.588 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:30.593 >>> print(''.join(results), end="")
2025-07-02 05:51:30.599 - abcDefghiJkl
2025-07-02 05:51:30.613 + abcdefGhijkl
2025-07-02 05:51:30.627 """
2025-07-02 05:51:30.633
2025-07-02 05:51:30.639 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:30.645 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:30.654 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:30.663 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:30.672 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:30.680
2025-07-02 05:51:30.687 # search for the pair that matches best without being identical
2025-07-02 05:51:30.694 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:30.701 # on junk -- unless we have to)
2025-07-02 05:51:30.707 for j in range(blo, bhi):
2025-07-02 05:51:30.712 bj = b[j]
2025-07-02 05:51:30.717 cruncher.set_seq2(bj)
2025-07-02 05:51:30.723 for i in range(alo, ahi):
2025-07-02 05:51:30.731 ai = a[i]
2025-07-02 05:51:30.739 if ai == bj:
2025-07-02 05:51:30.747 if eqi is None:
2025-07-02 05:51:30.755 eqi, eqj = i, j
2025-07-02 05:51:30.762 continue
2025-07-02 05:51:30.768 cruncher.set_seq1(ai)
2025-07-02 05:51:30.776 # computing similarity is expensive, so use the quick
2025-07-02 05:51:30.784 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:30.791 # compares by a factor of 3.
2025-07-02 05:51:30.797 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:30.803 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:30.811 # of the computation is cached by cruncher
2025-07-02 05:51:30.823 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:30.834 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:30.843 cruncher.ratio() > best_ratio:
2025-07-02 05:51:30.851 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:30.859 if best_ratio < cutoff:
2025-07-02 05:51:30.866 # no non-identical "pretty close" pair
2025-07-02 05:51:30.875 if eqi is None:
2025-07-02 05:51:30.884 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:30.901 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:30.911 return
2025-07-02 05:51:30.919 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:30.926 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:30.932 else:
2025-07-02 05:51:30.937 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:30.942 eqi = None
2025-07-02 05:51:30.947
2025-07-02 05:51:30.952 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:30.958 # identical
2025-07-02 05:51:30.970
2025-07-02 05:51:30.982 # pump out diffs from before the synch point
2025-07-02 05:51:30.990 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:31.001
2025-07-02 05:51:31.011 # do intraline marking on the synch pair
2025-07-02 05:51:31.019 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:31.027 if eqi is None:
2025-07-02 05:51:31.034 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:31.042 atags = btags = ""
2025-07-02 05:51:31.053 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:31.065 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:31.076 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:31.083 if tag == 'replace':
2025-07-02 05:51:31.091 atags += '^' * la
2025-07-02 05:51:31.097 btags += '^' * lb
2025-07-02 05:51:31.103 elif tag == 'delete':
2025-07-02 05:51:31.108 atags += '-' * la
2025-07-02 05:51:31.114 elif tag == 'insert':
2025-07-02 05:51:31.119 btags += '+' * lb
2025-07-02 05:51:31.124 elif tag == 'equal':
2025-07-02 05:51:31.129 atags += ' ' * la
2025-07-02 05:51:31.134 btags += ' ' * lb
2025-07-02 05:51:31.149 else:
2025-07-02 05:51:31.161 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:31.170 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:31.177 else:
2025-07-02 05:51:31.184 # the synch pair is identical
2025-07-02 05:51:31.190 yield '  ' + aelt
2025-07-02 05:51:31.196
2025-07-02 05:51:31.205 # pump out diffs from after the synch point
2025-07-02 05:51:31.217 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:31.225
2025-07-02 05:51:31.232 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:31.245 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:31.260
2025-07-02 05:51:31.273 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:31.284 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:31.292 alo = 383, ahi = 1101
2025-07-02 05:51:31.302 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:31.309 blo = 383, bhi = 1101
2025-07-02 05:51:31.318
2025-07-02 05:51:31.330 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:31.343 g = []
2025-07-02 05:51:31.353 if alo < ahi:
2025-07-02 05:51:31.361 if blo < bhi:
2025-07-02 05:51:31.374 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:31.383 else:
2025-07-02 05:51:31.396 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:31.408 elif blo < bhi:
2025-07-02 05:51:31.417 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:31.426
2025-07-02 05:51:31.433 >       yield from g
2025-07-02 05:51:31.439
2025-07-02 05:51:31.453 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:31.463 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:31.470
2025-07-02 05:51:31.477 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:31.492 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:31.503 alo = 383, ahi = 1101
2025-07-02 05:51:31.516 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:31.530 blo = 383, bhi = 1101
2025-07-02 05:51:31.543
2025-07-02 05:51:31.555 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:31.564 r"""
2025-07-02 05:51:31.572 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:31.580 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:31.587 synch point, and intraline difference marking is done on the
2025-07-02 05:51:31.595 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:31.608
2025-07-02 05:51:31.620 Example:
2025-07-02 05:51:31.629
2025-07-02 05:51:31.636 >>> d = Differ()
2025-07-02 05:51:31.643 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:31.650 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:31.657 >>> print(''.join(results), end="")
2025-07-02 05:51:31.665 - abcDefghiJkl
2025-07-02 05:51:31.679 + abcdefGhijkl
2025-07-02 05:51:31.695 """
2025-07-02 05:51:31.707
2025-07-02 05:51:31.722 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:31.733 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:31.740 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:31.747 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:31.752 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:31.758
2025-07-02 05:51:31.770 # search for the pair that matches best without being identical
2025-07-02 05:51:31.783 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:31.797 # on junk -- unless we have to)
2025-07-02 05:51:31.812 for j in range(blo, bhi):
2025-07-02 05:51:31.823 bj = b[j]
2025-07-02 05:51:31.835 cruncher.set_seq2(bj)
2025-07-02 05:51:31.844 for i in range(alo, ahi):
2025-07-02 05:51:31.858 ai = a[i]
2025-07-02 05:51:31.868 if ai == bj:
2025-07-02 05:51:31.877 if eqi is None:
2025-07-02 05:51:31.885 eqi, eqj = i, j
2025-07-02 05:51:31.892 continue
2025-07-02 05:51:31.899 cruncher.set_seq1(ai)
2025-07-02 05:51:31.907 # computing similarity is expensive, so use the quick
2025-07-02 05:51:31.914 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:31.923 # compares by a factor of 3.
2025-07-02 05:51:31.935 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:31.945 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:31.953 # of the computation is cached by cruncher
2025-07-02 05:51:31.959 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:31.965 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:31.970 cruncher.ratio() > best_ratio:
2025-07-02 05:51:31.975 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:31.980 if best_ratio < cutoff:
2025-07-02 05:51:31.986 # no non-identical "pretty close" pair
2025-07-02 05:51:31.994 if eqi is None:
2025-07-02 05:51:32.002 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:32.010 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:32.016 return
2025-07-02 05:51:32.023 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:32.031 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:32.037 else:
2025-07-02 05:51:32.043 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:32.051 eqi = None
2025-07-02 05:51:32.061
2025-07-02 05:51:32.068 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:32.074 # identical
2025-07-02 05:51:32.080
2025-07-02 05:51:32.086 # pump out diffs from before the synch point
2025-07-02 05:51:32.092 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:32.098
2025-07-02 05:51:32.108 # do intraline marking on the synch pair
2025-07-02 05:51:32.121 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:32.132 if eqi is None:
2025-07-02 05:51:32.144 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:32.154 atags = btags = ""
2025-07-02 05:51:32.165 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:32.179 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:32.190 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:32.199 if tag == 'replace':
2025-07-02 05:51:32.206 atags += '^' * la
2025-07-02 05:51:32.220 btags += '^' * lb
2025-07-02 05:51:32.230 elif tag == 'delete':
2025-07-02 05:51:32.239 atags += '-' * la
2025-07-02 05:51:32.247 elif tag == 'insert':
2025-07-02 05:51:32.260 btags += '+' * lb
2025-07-02 05:51:32.273 elif tag == 'equal':
2025-07-02 05:51:32.285 atags += ' ' * la
2025-07-02 05:51:32.296 btags += ' ' * lb
2025-07-02 05:51:32.304 else:
2025-07-02 05:51:32.311 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:32.324 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:32.336 else:
2025-07-02 05:51:32.347 # the synch pair is identical
2025-07-02 05:51:32.358 yield '  ' + aelt
2025-07-02 05:51:32.368
2025-07-02 05:51:32.377 # pump out diffs from after the synch point
2025-07-02 05:51:32.389 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:32.399
2025-07-02 05:51:32.407 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:32.415 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:32.422
2025-07-02 05:51:32.434 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:32.445 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:32.453 alo = 384, ahi = 1101
2025-07-02 05:51:32.462 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:32.473 blo = 384, bhi = 1101
2025-07-02 05:51:32.483
2025-07-02 05:51:32.492 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:32.500 g = []
2025-07-02 05:51:32.506 if alo < ahi:
2025-07-02 05:51:32.517 if blo < bhi:
2025-07-02 05:51:32.527 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:32.536 else:
2025-07-02 05:51:32.543 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:32.549 elif blo < bhi:
2025-07-02 05:51:32.555 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:32.564
2025-07-02 05:51:32.571 >       yield from g
2025-07-02 05:51:32.576
2025-07-02 05:51:32.580 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:32.585 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:32.590
2025-07-02 05:51:32.594 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:32.599 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:32.604 alo = 384, ahi = 1101
2025-07-02 05:51:32.609 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:32.614 blo = 384, bhi = 1101
2025-07-02 05:51:32.619
2025-07-02 05:51:32.623 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:32.628 r"""
2025-07-02 05:51:32.634 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:32.639 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:32.644 synch point, and intraline difference marking is done on the
2025-07-02 05:51:32.649 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:32.654
2025-07-02 05:51:32.659 Example:
2025-07-02 05:51:32.664
2025-07-02 05:51:32.669 >>> d = Differ()
2025-07-02 05:51:32.675 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:32.682 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:32.687 >>> print(''.join(results), end="")
2025-07-02 05:51:32.693 - abcDefghiJkl
2025-07-02 05:51:32.706 + abcdefGhijkl
2025-07-02 05:51:32.716 """
2025-07-02 05:51:32.722
2025-07-02 05:51:32.729 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:32.734 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:32.739 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:32.744 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:32.750 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:32.755
2025-07-02 05:51:32.761 # search for the pair that matches best without being identical
2025-07-02 05:51:32.768 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:32.774 # on junk -- unless we have to)
2025-07-02 05:51:32.781 for j in range(blo, bhi):
2025-07-02 05:51:32.787 bj = b[j]
2025-07-02 05:51:32.792 cruncher.set_seq2(bj)
2025-07-02 05:51:32.797 for i in range(alo, ahi):
2025-07-02 05:51:32.802 ai = a[i]
2025-07-02 05:51:32.807 if ai == bj:
2025-07-02 05:51:32.812 if eqi is None:
2025-07-02 05:51:32.819 eqi, eqj = i, j
2025-07-02 05:51:32.825 continue
2025-07-02 05:51:32.835 cruncher.set_seq1(ai)
2025-07-02 05:51:32.847 # computing similarity is expensive, so use the quick
2025-07-02 05:51:32.857 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:32.864 # compares by a factor of 3.
2025-07-02 05:51:32.870 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:32.879 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:32.891 # of the computation is cached by cruncher
2025-07-02 05:51:32.900 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:32.907 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:32.913 cruncher.ratio() > best_ratio:
2025-07-02 05:51:32.918 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:32.928 if best_ratio < cutoff:
2025-07-02 05:51:32.938 # no non-identical "pretty close" pair
2025-07-02 05:51:32.945 if eqi is None:
2025-07-02 05:51:32.955 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:32.964 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:32.970 return
2025-07-02 05:51:32.976 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:32.982 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:32.987 else:
2025-07-02 05:51:32.995 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:33.002 eqi = None
2025-07-02 05:51:33.010
2025-07-02 05:51:33.019 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:33.028 # identical
2025-07-02 05:51:33.038
2025-07-02 05:51:33.051 # pump out diffs from before the synch point
2025-07-02 05:51:33.064 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:33.074
2025-07-02 05:51:33.083 # do intraline marking on the synch pair
2025-07-02 05:51:33.090 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:33.098 if eqi is None:
2025-07-02 05:51:33.105 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:33.111 atags = btags = ""
2025-07-02 05:51:33.117 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:33.123 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:33.129 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:33.140 if tag == 'replace':
2025-07-02 05:51:33.151 atags += '^' * la
2025-07-02 05:51:33.160 btags += '^' * lb
2025-07-02 05:51:33.167 elif tag == 'delete':
2025-07-02 05:51:33.172 atags += '-' * la
2025-07-02 05:51:33.178 elif tag == 'insert':
2025-07-02 05:51:33.184 btags += '+' * lb
2025-07-02 05:51:33.190 elif tag == 'equal':
2025-07-02 05:51:33.200 atags += ' ' * la
2025-07-02 05:51:33.211 btags += ' ' * lb
2025-07-02 05:51:33.219 else:
2025-07-02 05:51:33.227 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:33.234 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:33.240 else:
2025-07-02 05:51:33.246 # the synch pair is identical
2025-07-02 05:51:33.252 yield '  ' + aelt
2025-07-02 05:51:33.265
2025-07-02 05:51:33.275 # pump out diffs from after the synch point
2025-07-02 05:51:33.283 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:33.289
2025-07-02 05:51:33.295 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:33.301 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:33.311
2025-07-02 05:51:33.322 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:33.332 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:33.340 alo = 385, ahi = 1101
2025-07-02 05:51:33.348 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:33.354 blo = 385, bhi = 1101
2025-07-02 05:51:33.359
2025-07-02 05:51:33.365 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:33.371 g = []
2025-07-02 05:51:33.378 if alo < ahi:
2025-07-02 05:51:33.384 if blo < bhi:
2025-07-02 05:51:33.392 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:33.399 else:
2025-07-02 05:51:33.411 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:33.420 elif blo < bhi:
2025-07-02 05:51:33.426 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:33.432
2025-07-02 05:51:33.439 >       yield from g
2025-07-02 05:51:33.444
2025-07-02 05:51:33.451 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:33.465 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:33.476
2025-07-02 05:51:33.487 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:33.501 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:33.514 alo = 385, ahi = 1101
2025-07-02 05:51:33.527 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:33.537 blo = 385, bhi = 1101
2025-07-02 05:51:33.544
2025-07-02 05:51:33.551 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:33.564 r"""
2025-07-02 05:51:33.574 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:33.581 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:33.593 synch point, and intraline difference marking is done on the
2025-07-02 05:51:33.604 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:33.617
2025-07-02 05:51:33.629 Example:
2025-07-02 05:51:33.638
2025-07-02 05:51:33.647 >>> d = Differ()
2025-07-02 05:51:33.657 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:33.667 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:33.675 >>> print(''.join(results), end="")
2025-07-02 05:51:33.682 - abcDefghiJkl
2025-07-02 05:51:33.693 + abcdefGhijkl
2025-07-02 05:51:33.703 """
2025-07-02 05:51:33.709
2025-07-02 05:51:33.715 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:33.722 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:33.728 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:33.744 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:33.759 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:33.768
2025-07-02 05:51:33.776 # search for the pair that matches best without being identical
2025-07-02 05:51:33.783 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:33.790 # on junk -- unless we have to)
2025-07-02 05:51:33.798 for j in range(blo, bhi):
2025-07-02 05:51:33.805 bj = b[j]
2025-07-02 05:51:33.812 cruncher.set_seq2(bj)
2025-07-02 05:51:33.819 for i in range(alo, ahi):
2025-07-02 05:51:33.826 ai = a[i]
2025-07-02 05:51:33.833 if ai == bj:
2025-07-02 05:51:33.840 if eqi is None:
2025-07-02 05:51:33.848 eqi, eqj = i, j
2025-07-02 05:51:33.856 continue
2025-07-02 05:51:33.864 cruncher.set_seq1(ai)
2025-07-02 05:51:33.873 # computing similarity is expensive, so use the quick
2025-07-02 05:51:33.881 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:33.890 # compares by a factor of 3.
2025-07-02 05:51:33.898 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:33.906 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:33.912 # of the computation is cached by cruncher
2025-07-02 05:51:33.921 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:33.928 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:33.935 cruncher.ratio() > best_ratio:
2025-07-02 05:51:33.942 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:33.949 if best_ratio < cutoff:
2025-07-02 05:51:33.956 # no non-identical "pretty close" pair
2025-07-02 05:51:33.962 if eqi is None:
2025-07-02 05:51:33.978 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:33.989 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:33.999 return
2025-07-02 05:51:34.006 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:34.013 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:34.020 else:
2025-07-02 05:51:34.027 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:34.035 eqi = None
2025-07-02 05:51:34.042
2025-07-02 05:51:34.051 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:34.059 # identical
2025-07-02 05:51:34.067
2025-07-02 05:51:34.075 # pump out diffs from before the synch point
2025-07-02 05:51:34.083 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:34.091
2025-07-02 05:51:34.098 # do intraline marking on the synch pair
2025-07-02 05:51:34.105 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:34.113 if eqi is None:
2025-07-02 05:51:34.122 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:34.129 atags = btags = ""
2025-07-02 05:51:34.136 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:34.143 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:34.153 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:34.162 if tag == 'replace':
2025-07-02 05:51:34.169 atags += '^' * la
2025-07-02 05:51:34.174 btags += '^' * lb
2025-07-02 05:51:34.180 elif tag == 'delete':
2025-07-02 05:51:34.186 atags += '-' * la
2025-07-02 05:51:34.195 elif tag == 'insert':
2025-07-02 05:51:34.201 btags += '+' * lb
2025-07-02 05:51:34.209 elif tag == 'equal':
2025-07-02 05:51:34.220 atags += ' ' * la
2025-07-02 05:51:34.232 btags += ' ' * lb
2025-07-02 05:51:34.243 else:
2025-07-02 05:51:34.251 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:34.261 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:34.269 else:
2025-07-02 05:51:34.276 # the synch pair is identical
2025-07-02 05:51:34.283 yield '  ' + aelt
2025-07-02 05:51:34.290
2025-07-02 05:51:34.297 # pump out diffs from after the synch point
2025-07-02 05:51:34.303 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:34.310
2025-07-02 05:51:34.317 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:34.324 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:34.331
2025-07-02 05:51:34.337 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:34.345 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:34.359 alo = 386, ahi = 1101
2025-07-02 05:51:34.369 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:34.376 blo = 386, bhi = 1101
2025-07-02 05:51:34.387
2025-07-02 05:51:34.400 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:34.410 g = []
2025-07-02 05:51:34.425 if alo < ahi:
2025-07-02 05:51:34.433 if blo < bhi:
2025-07-02 05:51:34.441 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:34.449 else:
2025-07-02 05:51:34.455 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:34.462 elif blo < bhi:
2025-07-02 05:51:34.471 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:34.484
2025-07-02 05:51:34.497 >       yield from g
2025-07-02 05:51:34.507
2025-07-02 05:51:34.513 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:34.520 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:34.527
2025-07-02 05:51:34.534 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:34.542 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:34.551 alo = 386, ahi = 1101
2025-07-02 05:51:34.566 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:34.573 blo = 386, bhi = 1101
2025-07-02 05:51:34.579
2025-07-02 05:51:34.591 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:34.600 r"""
2025-07-02 05:51:34.607 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:34.614 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:34.622 synch point, and intraline difference marking is done on the
2025-07-02 05:51:34.628 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:34.634
2025-07-02 05:51:34.645 Example:
2025-07-02 05:51:34.659
2025-07-02 05:51:34.672 >>> d = Differ()
2025-07-02 05:51:34.681 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:34.695 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:34.705 >>> print(''.join(results), end="")
2025-07-02 05:51:34.712 - abcDefghiJkl
2025-07-02 05:51:34.724 + abcdefGhijkl
2025-07-02 05:51:34.737 """
2025-07-02 05:51:34.743
2025-07-02 05:51:34.750 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:34.758 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:34.765 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:34.772 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:34.778 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:34.786
2025-07-02 05:51:34.798 # search for the pair that matches best without being identical
2025-07-02 05:51:34.807 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:34.814 # on junk -- unless we have to)
2025-07-02 05:51:34.825 for j in range(blo, bhi):
2025-07-02 05:51:34.836 bj = b[j]
2025-07-02 05:51:34.843 cruncher.set_seq2(bj)
2025-07-02 05:51:34.851 for i in range(alo, ahi):
2025-07-02 05:51:34.858 ai = a[i]
2025-07-02 05:51:34.868 if ai == bj:
2025-07-02 05:51:34.876 if eqi is None:
2025-07-02 05:51:34.884 eqi, eqj = i, j
2025-07-02 05:51:34.892 continue
2025-07-02 05:51:34.899 cruncher.set_seq1(ai)
2025-07-02 05:51:34.904 # computing similarity is expensive, so use the quick
2025-07-02 05:51:34.910 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:34.915 # compares by a factor of 3.
2025-07-02 05:51:34.923 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:34.934 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:34.942 # of the computation is cached by cruncher
2025-07-02 05:51:34.954 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:34.963 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:34.971 cruncher.ratio() > best_ratio:
2025-07-02 05:51:34.979 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:34.986 if best_ratio < cutoff:
2025-07-02 05:51:34.994 # no non-identical "pretty close" pair
2025-07-02 05:51:35.007 if eqi is None:
2025-07-02 05:51:35.017 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:35.024 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:35.035 return
2025-07-02 05:51:35.047 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:35.057 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:35.064 else:
2025-07-02 05:51:35.071 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:35.077 eqi = None
2025-07-02 05:51:35.082
2025-07-02 05:51:35.091 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:35.103 # identical
2025-07-02 05:51:35.111
2025-07-02 05:51:35.121 # pump out diffs from before the synch point
2025-07-02 05:51:35.129 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:35.135
2025-07-02 05:51:35.145 # do intraline marking on the synch pair
2025-07-02 05:51:35.158 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:35.167 if eqi is None:
2025-07-02 05:51:35.180 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:35.190 atags = btags = ""
2025-07-02 05:51:35.202 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:35.211 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:35.218 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:35.226 if tag == 'replace':
2025-07-02 05:51:35.232 atags += '^' * la
2025-07-02 05:51:35.239 btags += '^' * lb
2025-07-02 05:51:35.245 elif tag == 'delete':
2025-07-02 05:51:35.250 atags += '-' * la
2025-07-02 05:51:35.256 elif tag == 'insert':
2025-07-02 05:51:35.262 btags += '+' * lb
2025-07-02 05:51:35.269 elif tag == 'equal':
2025-07-02 05:51:35.275 atags += ' ' * la
2025-07-02 05:51:35.281 btags += ' ' * lb
2025-07-02 05:51:35.287 else:
2025-07-02 05:51:35.295 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:35.306 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:35.314 else:
2025-07-02 05:51:35.321 # the synch pair is identical
2025-07-02 05:51:35.327 yield '  ' + aelt
2025-07-02 05:51:35.336
2025-07-02 05:51:35.348 # pump out diffs from after the synch point
2025-07-02 05:51:35.356 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:35.364
2025-07-02 05:51:35.370 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:35.377 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:35.382
2025-07-02 05:51:35.387 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:35.394 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:35.400 alo = 387, ahi = 1101
2025-07-02 05:51:35.415 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:35.426 blo = 387, bhi = 1101
2025-07-02 05:51:35.439
2025-07-02 05:51:35.450 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:35.457 g = []
2025-07-02 05:51:35.464 if alo < ahi:
2025-07-02 05:51:35.471 if blo < bhi:
2025-07-02 05:51:35.485 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:35.493 else:
2025-07-02 05:51:35.500 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:35.508 elif blo < bhi:
2025-07-02 05:51:35.514 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:35.525
2025-07-02 05:51:35.536 >       yield from g
2025-07-02 05:51:35.547
2025-07-02 05:51:35.558 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:35.570 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:35.583
2025-07-02 05:51:35.594 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:35.604 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:35.611 alo = 387, ahi = 1101
2025-07-02 05:51:35.619 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:35.627 blo = 387, bhi = 1101
2025-07-02 05:51:35.638
2025-07-02 05:51:35.647 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:35.654 r"""
2025-07-02 05:51:35.666 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:35.677 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:35.690 synch point, and intraline difference marking is done on the
2025-07-02 05:51:35.706 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:35.717
2025-07-02 05:51:35.725 Example:
2025-07-02 05:51:35.740
2025-07-02 05:51:35.751 >>> d = Differ()
2025-07-02 05:51:35.759 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:35.766 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:35.771 >>> print(''.join(results), end="")
2025-07-02 05:51:35.777 - abcDefghiJkl
2025-07-02 05:51:35.788 + abcdefGhijkl
2025-07-02 05:51:35.798 """
2025-07-02 05:51:35.806
2025-07-02 05:51:35.814 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:35.820 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:35.829 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:35.837 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:35.845 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:35.851
2025-07-02 05:51:35.858 # search for the pair that matches best without being identical
2025-07-02 05:51:35.868 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:35.879 # on junk -- unless we have to)
2025-07-02 05:51:35.891 for j in range(blo, bhi):
2025-07-02 05:51:35.899 bj = b[j]
2025-07-02 05:51:35.913 cruncher.set_seq2(bj)
2025-07-02 05:51:35.930 for i in range(alo, ahi):
2025-07-02 05:51:35.944 ai = a[i]
2025-07-02 05:51:35.958 if ai == bj:
2025-07-02 05:51:35.968 if eqi is None:
2025-07-02 05:51:35.976 eqi, eqj = i, j
2025-07-02 05:51:35.983 continue
2025-07-02 05:51:35.990 cruncher.set_seq1(ai)
2025-07-02 05:51:35.997 # computing similarity is expensive, so use the quick
2025-07-02 05:51:36.003 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:36.011 # compares by a factor of 3.
2025-07-02 05:51:36.022 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:36.029 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:36.035 # of the computation is cached by cruncher
2025-07-02 05:51:36.043 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:36.054 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:36.064 cruncher.ratio() > best_ratio:
2025-07-02 05:51:36.078 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:36.089 if best_ratio < cutoff:
2025-07-02 05:51:36.098 # no non-identical "pretty close" pair
2025-07-02 05:51:36.111 if eqi is None:
2025-07-02 05:51:36.124 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:36.134 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:36.141 return
2025-07-02 05:51:36.149 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:36.163 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:36.172 else:
2025-07-02 05:51:36.180 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:36.187 eqi = None
2025-07-02 05:51:36.194
2025-07-02 05:51:36.207 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:36.220 # identical
2025-07-02 05:51:36.231
2025-07-02 05:51:36.242 # pump out diffs from before the synch point
2025-07-02 05:51:36.250 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:36.256
2025-07-02 05:51:36.263 # do intraline marking on the synch pair
2025-07-02 05:51:36.271 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:36.279 if eqi is None:
2025-07-02 05:51:36.285 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:36.292 atags = btags = ""
2025-07-02 05:51:36.306 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:36.314 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:36.323 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:36.337 if tag == 'replace':
2025-07-02 05:51:36.350 atags += '^' * la
2025-07-02 05:51:36.360 btags += '^' * lb
2025-07-02 05:51:36.368 elif tag == 'delete':
2025-07-02 05:51:36.376 atags += '-' * la
2025-07-02 05:51:36.383 elif tag == 'insert':
2025-07-02 05:51:36.393 btags += '+' * lb
2025-07-02 05:51:36.405 elif tag == 'equal':
2025-07-02 05:51:36.416 atags += ' ' * la
2025-07-02 05:51:36.426 btags += ' ' * lb
2025-07-02 05:51:36.438 else:
2025-07-02 05:51:36.451 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:36.467 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:36.478 else:
2025-07-02 05:51:36.485 # the synch pair is identical
2025-07-02 05:51:36.490 yield '  ' + aelt
2025-07-02 05:51:36.503
2025-07-02 05:51:36.516 # pump out diffs from after the synch point
2025-07-02 05:51:36.530 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:36.541
2025-07-02 05:51:36.552 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:36.564 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:36.574
2025-07-02 05:51:36.588 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:36.599 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:36.608 alo = 388, ahi = 1101
2025-07-02 05:51:36.617 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:36.624 blo = 388, bhi = 1101
2025-07-02 05:51:36.630
2025-07-02 05:51:36.635 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:36.640 g = []
2025-07-02 05:51:36.644 if alo < ahi:
2025-07-02 05:51:36.649 if blo < bhi:
2025-07-02 05:51:36.656 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:36.662 else:
2025-07-02 05:51:36.669 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:36.681 elif blo < bhi:
2025-07-02 05:51:36.692 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:36.702
2025-07-02 05:51:36.713 >       yield from g
2025-07-02 05:51:36.722
2025-07-02 05:51:36.729 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:36.741 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:36.752
2025-07-02 05:51:36.762 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:36.776 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:36.789 alo = 388, ahi = 1101
2025-07-02 05:51:36.798 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:36.805 blo = 388, bhi = 1101
2025-07-02 05:51:36.815
2025-07-02 05:51:36.825 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:36.835 r"""
2025-07-02 05:51:36.842 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:36.851 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:36.857 synch point, and intraline difference marking is done on the
2025-07-02 05:51:36.863 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:36.870
2025-07-02 05:51:36.879 Example:
2025-07-02 05:51:36.889
2025-07-02 05:51:36.896 >>> d = Differ()
2025-07-02 05:51:36.904 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:36.918 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:36.930 >>> print(''.join(results), end="")
2025-07-02 05:51:36.938 - abcDefghiJkl
2025-07-02 05:51:36.951 + abcdefGhijkl
2025-07-02 05:51:36.965 """
2025-07-02 05:51:36.971
2025-07-02 05:51:36.979 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:36.992 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:37.003 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:37.013 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:37.022 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:37.033
2025-07-02 05:51:37.044 # search for the pair that matches best without being identical
2025-07-02 05:51:37.052 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:37.060 # on junk -- unless we have to)
2025-07-02 05:51:37.066 for j in range(blo, bhi):
2025-07-02 05:51:37.073 bj = b[j]
2025-07-02 05:51:37.079 cruncher.set_seq2(bj)
2025-07-02 05:51:37.087 for i in range(alo, ahi):
2025-07-02 05:51:37.094 ai = a[i]
2025-07-02 05:51:37.101 if ai == bj:
2025-07-02 05:51:37.113 if eqi is None:
2025-07-02 05:51:37.123 eqi, eqj = i, j
2025-07-02 05:51:37.137 continue
2025-07-02 05:51:37.147 cruncher.set_seq1(ai)
2025-07-02 05:51:37.156 # computing similarity is expensive, so use the quick
2025-07-02 05:51:37.165 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:37.173 # compares by a factor of 3.
2025-07-02 05:51:37.179 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:37.187 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:37.198 # of the computation is cached by cruncher
2025-07-02 05:51:37.209 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:37.221 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:37.231 cruncher.ratio() > best_ratio:
2025-07-02 05:51:37.241 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:37.249 if best_ratio < cutoff:
2025-07-02 05:51:37.257 # no non-identical "pretty close" pair
2025-07-02 05:51:37.266 if eqi is None:
2025-07-02 05:51:37.275 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:37.285 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:37.293 return
2025-07-02 05:51:37.301 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:37.311 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:37.323 else:
2025-07-02 05:51:37.332 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:37.339 eqi = None
2025-07-02 05:51:37.346
2025-07-02 05:51:37.356 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:37.364 # identical
2025-07-02 05:51:37.371
2025-07-02 05:51:37.378 # pump out diffs from before the synch point
2025-07-02 05:51:37.384 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:37.390
2025-07-02 05:51:37.396 # do intraline marking on the synch pair
2025-07-02 05:51:37.403 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:37.411 if eqi is None:
2025-07-02 05:51:37.421 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:37.428 atags = btags = ""
2025-07-02 05:51:37.435 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:37.442 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:37.449 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:37.456 if tag == 'replace':
2025-07-02 05:51:37.463 atags += '^' * la
2025-07-02 05:51:37.470 btags += '^' * lb
2025-07-02 05:51:37.478 elif tag == 'delete':
2025-07-02 05:51:37.484 atags += '-' * la
2025-07-02 05:51:37.490 elif tag == 'insert':
2025-07-02 05:51:37.500 btags += '+' * lb
2025-07-02 05:51:37.513 elif tag == 'equal':
2025-07-02 05:51:37.523 atags += ' ' * la
2025-07-02 05:51:37.531 btags += ' ' * lb
2025-07-02 05:51:37.540 else:
2025-07-02 05:51:37.551 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:37.560 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:37.568 else:
2025-07-02 05:51:37.575 # the synch pair is identical
2025-07-02 05:51:37.582 yield '  ' + aelt
2025-07-02 05:51:37.589
2025-07-02 05:51:37.594 # pump out diffs from after the synch point
2025-07-02 05:51:37.599 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:37.604
2025-07-02 05:51:37.617 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:37.627 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:37.634
2025-07-02 05:51:37.640 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:37.648 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:37.656 alo = 389, ahi = 1101
2025-07-02 05:51:37.665 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:37.672 blo = 389, bhi = 1101
2025-07-02 05:51:37.679
2025-07-02 05:51:37.687 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:37.695 g = []
2025-07-02 05:51:37.707 if alo < ahi:
2025-07-02 05:51:37.715 if blo < bhi:
2025-07-02 05:51:37.722 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:37.729 else:
2025-07-02 05:51:37.737 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:37.743 elif blo < bhi:
2025-07-02 05:51:37.749 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:37.756
2025-07-02 05:51:37.762 >       yield from g
2025-07-02 05:51:37.768
2025-07-02 05:51:37.773 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:37.785 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:37.798
2025-07-02 05:51:37.806 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:37.813 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:37.819 alo = 389, ahi = 1101
2025-07-02 05:51:37.824 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:37.829 blo = 389, bhi = 1101
2025-07-02 05:51:37.833
2025-07-02 05:51:37.838 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:37.843 r"""
2025-07-02 05:51:37.856 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:37.867 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:37.881 synch point, and intraline difference marking is done on the
2025-07-02 05:51:37.891 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:37.899
2025-07-02 05:51:37.906 Example:
2025-07-02 05:51:37.919
2025-07-02 05:51:37.931 >>> d = Differ()
2025-07-02 05:51:37.941 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:37.950 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:37.959 >>> print(''.join(results), end="")
2025-07-02 05:51:37.966 - abcDefghiJkl
2025-07-02 05:51:37.980 + abcdefGhijkl
2025-07-02 05:51:37.993 """
2025-07-02 05:51:38.000
2025-07-02 05:51:38.010 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:38.018 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:38.025 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:38.033 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:38.046 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:38.056
2025-07-02 05:51:38.068 # search for the pair that matches best without being identical
2025-07-02 05:51:38.080 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:38.088 # on junk -- unless we have to)
2025-07-02 05:51:38.094 for j in range(blo, bhi):
2025-07-02 05:51:38.101 bj = b[j]
2025-07-02 05:51:38.108 cruncher.set_seq2(bj)
2025-07-02 05:51:38.115 for i in range(alo, ahi):
2025-07-02 05:51:38.121 ai = a[i]
2025-07-02 05:51:38.127 if ai == bj:
2025-07-02 05:51:38.134 if eqi is None:
2025-07-02 05:51:38.139 eqi, eqj = i, j
2025-07-02 05:51:38.144 continue
2025-07-02 05:51:38.150 cruncher.set_seq1(ai)
2025-07-02 05:51:38.158 # computing similarity is expensive, so use the quick
2025-07-02 05:51:38.165 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:38.172 # compares by a factor of 3.
2025-07-02 05:51:38.183 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:38.191 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:38.199 # of the computation is cached by cruncher
2025-07-02 05:51:38.205 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:38.210 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:38.217 cruncher.ratio() > best_ratio:
2025-07-02 05:51:38.229 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:38.238 if best_ratio < cutoff:
2025-07-02 05:51:38.247 # no non-identical "pretty close" pair
2025-07-02 05:51:38.254 if eqi is None:
2025-07-02 05:51:38.260 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:38.267 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:38.273 return
2025-07-02 05:51:38.280 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:38.287 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:38.293 else:
2025-07-02 05:51:38.299 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:38.311 eqi = None
2025-07-02 05:51:38.320
2025-07-02 05:51:38.329 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:38.336 # identical
2025-07-02 05:51:38.343
2025-07-02 05:51:38.349 # pump out diffs from before the synch point
2025-07-02 05:51:38.356 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:38.369
2025-07-02 05:51:38.380 # do intraline marking on the synch pair
2025-07-02 05:51:38.389 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:38.401 if eqi is None:
2025-07-02 05:51:38.412 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:38.420 atags = btags = ""
2025-07-02 05:51:38.427 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:38.433 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:38.439 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:38.444 if tag == 'replace':
2025-07-02 05:51:38.449 atags += '^' * la
2025-07-02 05:51:38.456 btags += '^' * lb
2025-07-02 05:51:38.464 elif tag == 'delete':
2025-07-02 05:51:38.474 atags += '-' * la
2025-07-02 05:51:38.482 elif tag == 'insert':
2025-07-02 05:51:38.490 btags += '+' * lb
2025-07-02 05:51:38.501 elif tag == 'equal':
2025-07-02 05:51:38.513 atags += ' ' * la
2025-07-02 05:51:38.523 btags += ' ' * lb
2025-07-02 05:51:38.532 else:
2025-07-02 05:51:38.541 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:38.553 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:38.566 else:
2025-07-02 05:51:38.577 # the synch pair is identical
2025-07-02 05:51:38.585 yield '  ' + aelt
2025-07-02 05:51:38.592
2025-07-02 05:51:38.599 # pump out diffs from after the synch point
2025-07-02 05:51:38.612 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:38.623
2025-07-02 05:51:38.631 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:38.639 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:38.645
2025-07-02 05:51:38.651 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:38.660 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:38.666 alo = 390, ahi = 1101
2025-07-02 05:51:38.681 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:38.692 blo = 390, bhi = 1101
2025-07-02 05:51:38.705
2025-07-02 05:51:38.717 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:38.727 g = []
2025-07-02 05:51:38.736 if alo < ahi:
2025-07-02 05:51:38.745 if blo < bhi:
2025-07-02 05:51:38.756 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:38.768 else:
2025-07-02 05:51:38.777 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:38.792 elif blo < bhi:
2025-07-02 05:51:38.803 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:38.817
2025-07-02 05:51:38.830 >       yield from g
2025-07-02 05:51:38.841
2025-07-02 05:51:38.855 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:38.866 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:38.874
2025-07-02 05:51:38.881 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:38.890 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:38.900 alo = 390, ahi = 1101
2025-07-02 05:51:38.912 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:38.922 blo = 390, bhi = 1101
2025-07-02 05:51:38.929
2025-07-02 05:51:38.942 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:38.952 r"""
2025-07-02 05:51:38.961 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:38.969 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:38.977 synch point, and intraline difference marking is done on the
2025-07-02 05:51:38.989 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:38.999
2025-07-02 05:51:39.008 Example:
2025-07-02 05:51:39.015
2025-07-02 05:51:39.022 >>> d = Differ()
2025-07-02 05:51:39.029 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:39.039 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:39.049 >>> print(''.join(results), end="")
2025-07-02 05:51:39.061 - abcDefghiJkl
2025-07-02 05:51:39.078 + abcdefGhijkl
2025-07-02 05:51:39.092 """
2025-07-02 05:51:39.100
2025-07-02 05:51:39.108 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:39.115 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:39.123 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:39.131 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:39.139 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:39.148
2025-07-02 05:51:39.158 # search for the pair that matches best without being identical
2025-07-02 05:51:39.164 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:39.171 # on junk -- unless we have to)
2025-07-02 05:51:39.178 for j in range(blo, bhi):
2025-07-02 05:51:39.184 bj = b[j]
2025-07-02 05:51:39.191 cruncher.set_seq2(bj)
2025-07-02 05:51:39.198 for i in range(alo, ahi):
2025-07-02 05:51:39.204 ai = a[i]
2025-07-02 05:51:39.211 if ai == bj:
2025-07-02 05:51:39.218 if eqi is None:
2025-07-02 05:51:39.224 eqi, eqj = i, j
2025-07-02 05:51:39.230 continue
2025-07-02 05:51:39.235 cruncher.set_seq1(ai)
2025-07-02 05:51:39.240 # computing similarity is expensive, so use the quick
2025-07-02 05:51:39.245 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:39.250 # compares by a factor of 3.
2025-07-02 05:51:39.255 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:39.260 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:39.265 # of the computation is cached by cruncher
2025-07-02 05:51:39.278 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:39.288 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:39.295 cruncher.ratio() > best_ratio:
2025-07-02 05:51:39.301 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:39.307 if best_ratio < cutoff:
2025-07-02 05:51:39.314 # no non-identical "pretty close" pair
2025-07-02 05:51:39.321 if eqi is None:
2025-07-02 05:51:39.333 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:39.346 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:39.354 return
2025-07-02 05:51:39.361 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:39.368 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:39.375 else:
2025-07-02 05:51:39.383 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:39.392 eqi = None
2025-07-02 05:51:39.401
2025-07-02 05:51:39.413 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:39.424 # identical
2025-07-02 05:51:39.432
2025-07-02 05:51:39.442 # pump out diffs from before the synch point
2025-07-02 05:51:39.455 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:39.463
2025-07-02 05:51:39.470 # do intraline marking on the synch pair
2025-07-02 05:51:39.474 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:39.479 if eqi is None:
2025-07-02 05:51:39.491 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:39.498 atags = btags = ""
2025-07-02 05:51:39.513 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:39.524 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:39.533 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:39.541 if tag == 'replace':
2025-07-02 05:51:39.548 atags += '^' * la
2025-07-02 05:51:39.554 btags += '^' * lb
2025-07-02 05:51:39.558 elif tag == 'delete':
2025-07-02 05:51:39.563 atags += '-' * la
2025-07-02 05:51:39.569 elif tag == 'insert':
2025-07-02 05:51:39.577 btags += '+' * lb
2025-07-02 05:51:39.585 elif tag == 'equal':
2025-07-02 05:51:39.594 atags += ' ' * la
2025-07-02 05:51:39.605 btags += ' ' * lb
2025-07-02 05:51:39.614 else:
2025-07-02 05:51:39.622 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:39.629 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:39.635 else:
2025-07-02 05:51:39.643 # the synch pair is identical
2025-07-02 05:51:39.653 yield '  ' + aelt
2025-07-02 05:51:39.662
2025-07-02 05:51:39.671 # pump out diffs from after the synch point
2025-07-02 05:51:39.682 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:39.691
2025-07-02 05:51:39.698 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:39.707 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:39.718
2025-07-02 05:51:39.728 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:39.737 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:39.750 alo = 391, ahi = 1101
2025-07-02 05:51:39.764 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:39.773 blo = 391, bhi = 1101
2025-07-02 05:51:39.779
2025-07-02 05:51:39.787 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:39.796 g = []
2025-07-02 05:51:39.802 if alo < ahi:
2025-07-02 05:51:39.810 if blo < bhi:
2025-07-02 05:51:39.818 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:39.829 else:
2025-07-02 05:51:39.839 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:39.848 elif blo < bhi:
2025-07-02 05:51:39.855 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:39.865
2025-07-02 05:51:39.877 >       yield from g
2025-07-02 05:51:39.885
2025-07-02 05:51:39.892 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:39.900 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:39.908
2025-07-02 05:51:39.915 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:39.922 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:39.928 alo = 391, ahi = 1101
2025-07-02 05:51:39.933 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:39.938 blo = 391, bhi = 1101
2025-07-02 05:51:39.944
2025-07-02 05:51:39.950 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:39.956 r"""
2025-07-02 05:51:39.962 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:39.967 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:39.974 synch point, and intraline difference marking is done on the
2025-07-02 05:51:39.983 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:39.990
2025-07-02 05:51:39.996 Example:
2025-07-02 05:51:40.002
2025-07-02 05:51:40.010 >>> d = Differ()
2025-07-02 05:51:40.018 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:40.026 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:40.035 >>> print(''.join(results), end="")
2025-07-02 05:51:40.043 - abcDefghiJkl
2025-07-02 05:51:40.055 + abcdefGhijkl
2025-07-02 05:51:40.066 """
2025-07-02 05:51:40.074
2025-07-02 05:51:40.084 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:40.095 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:40.104 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:40.112 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:40.120 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:40.126
2025-07-02 05:51:40.133 # search for the pair that matches best without being identical
2025-07-02 05:51:40.139 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:40.145 # on junk -- unless we have to)
2025-07-02 05:51:40.151 for j in range(blo, bhi):
2025-07-02 05:51:40.157 bj = b[j]
2025-07-02 05:51:40.163 cruncher.set_seq2(bj)
2025-07-02 05:51:40.170 for i in range(alo, ahi):
2025-07-02 05:51:40.177 ai = a[i]
2025-07-02 05:51:40.183 if ai == bj:
2025-07-02 05:51:40.191 if eqi is None:
2025-07-02 05:51:40.204 eqi, eqj = i, j
2025-07-02 05:51:40.213 continue
2025-07-02 05:51:40.221 cruncher.set_seq1(ai)
2025-07-02 05:51:40.228 # computing similarity is expensive, so use the quick
2025-07-02 05:51:40.235 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:40.242 # compares by a factor of 3.
2025-07-02 05:51:40.252 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:40.262 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:40.269 # of the computation is cached by cruncher
2025-07-02 05:51:40.276 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:40.283 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:40.290 cruncher.ratio() > best_ratio:
2025-07-02 05:51:40.296 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:40.302 if best_ratio < cutoff:
2025-07-02 05:51:40.308 # no non-identical "pretty close" pair
2025-07-02 05:51:40.316 if eqi is None:
2025-07-02 05:51:40.324 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:40.331 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:40.337 return
2025-07-02 05:51:40.343 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:40.351 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:40.364 else:
2025-07-02 05:51:40.375 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:40.384 eqi = None
2025-07-02 05:51:40.391
2025-07-02 05:51:40.397 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:40.402 # identical
2025-07-02 05:51:40.408
2025-07-02 05:51:40.413 # pump out diffs from before the synch point
2025-07-02 05:51:40.418 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:40.423
2025-07-02 05:51:40.428 # do intraline marking on the synch pair
2025-07-02 05:51:40.434 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:40.442 if eqi is None:
2025-07-02 05:51:40.450 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:40.458 atags = btags = ""
2025-07-02 05:51:40.469 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:40.478 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:40.486 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:40.492 if tag == 'replace':
2025-07-02 05:51:40.498 atags += '^' * la
2025-07-02 05:51:40.504 btags += '^' * lb
2025-07-02 05:51:40.511 elif tag == 'delete':
2025-07-02 05:51:40.518 atags += '-' * la
2025-07-02 05:51:40.524 elif tag == 'insert':
2025-07-02 05:51:40.532 btags += '+' * lb
2025-07-02 05:51:40.539 elif tag == 'equal':
2025-07-02 05:51:40.546 atags += ' ' * la
2025-07-02 05:51:40.553 btags += ' ' * lb
2025-07-02 05:51:40.559 else:
2025-07-02 05:51:40.566 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:40.575 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:40.581 else:
2025-07-02 05:51:40.586 # the synch pair is identical
2025-07-02 05:51:40.592 yield '  ' + aelt
2025-07-02 05:51:40.597
2025-07-02 05:51:40.603 # pump out diffs from after the synch point
2025-07-02 05:51:40.612 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:40.622
2025-07-02 05:51:40.629 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:40.636 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:40.642
2025-07-02 05:51:40.653 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:40.665 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:40.672 alo = 392, ahi = 1101
2025-07-02 05:51:40.680 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:40.686 blo = 392, bhi = 1101
2025-07-02 05:51:40.691
2025-07-02 05:51:40.699 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:40.710 g = []
2025-07-02 05:51:40.718 if alo < ahi:
2025-07-02 05:51:40.729 if blo < bhi:
2025-07-02 05:51:40.740 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:40.749 else:
2025-07-02 05:51:40.757 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:40.764 elif blo < bhi:
2025-07-02 05:51:40.770 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:40.782
2025-07-02 05:51:40.797 >       yield from g
2025-07-02 05:51:40.809
2025-07-02 05:51:40.820 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:40.833 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:40.846
2025-07-02 05:51:40.856 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:40.867 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:40.877 alo = 392, ahi = 1101
2025-07-02 05:51:40.890 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:40.901 blo = 392, bhi = 1101
2025-07-02 05:51:40.910
2025-07-02 05:51:40.917 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:40.925 r"""
2025-07-02 05:51:40.934 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:40.941 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:40.948 synch point, and intraline difference marking is done on the
2025-07-02 05:51:40.958 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:40.965
2025-07-02 05:51:40.972 Example:
2025-07-02 05:51:40.979
2025-07-02 05:51:40.986 >>> d = Differ()
2025-07-02 05:51:40.994 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:41.001 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:41.008 >>> print(''.join(results), end="")
2025-07-02 05:51:41.014 - abcDefghiJkl
2025-07-02 05:51:41.027 + abcdefGhijkl
2025-07-02 05:51:41.043 """
2025-07-02 05:51:41.055
2025-07-02 05:51:41.065 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:41.075 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:41.083 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:41.092 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:41.100 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:41.108
2025-07-02 05:51:41.120 # search for the pair that matches best without being identical
2025-07-02 05:51:41.129 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:41.135 # on junk -- unless we have to)
2025-07-02 05:51:41.141 for j in range(blo, bhi):
2025-07-02 05:51:41.147 bj = b[j]
2025-07-02 05:51:41.154 cruncher.set_seq2(bj)
2025-07-02 05:51:41.161 for i in range(alo, ahi):
2025-07-02 05:51:41.168 ai = a[i]
2025-07-02 05:51:41.176 if ai == bj:
2025-07-02 05:51:41.184 if eqi is None:
2025-07-02 05:51:41.192 eqi, eqj = i, j
2025-07-02 05:51:41.200 continue
2025-07-02 05:51:41.208 cruncher.set_seq1(ai)
2025-07-02 05:51:41.216 # computing similarity is expensive, so use the quick
2025-07-02 05:51:41.224 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:41.231 # compares by a factor of 3.
2025-07-02 05:51:41.240 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:41.253 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:41.265 # of the computation is cached by cruncher
2025-07-02 05:51:41.272 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:41.278 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:41.284 cruncher.ratio() > best_ratio:
2025-07-02 05:51:41.289 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:41.294 if best_ratio < cutoff:
2025-07-02 05:51:41.298 # no non-identical "pretty close" pair
2025-07-02 05:51:41.303 if eqi is None:
2025-07-02 05:51:41.308 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:41.312 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:41.317 return
2025-07-02 05:51:41.321 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:41.326 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:41.331 else:
2025-07-02 05:51:41.336 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:41.341 eqi = None
2025-07-02 05:51:41.346
2025-07-02 05:51:41.353 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:41.360 # identical
2025-07-02 05:51:41.366
2025-07-02 05:51:41.373 # pump out diffs from before the synch point
2025-07-02 05:51:41.380 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:41.387
2025-07-02 05:51:41.395 # do intraline marking on the synch pair
2025-07-02 05:51:41.405 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:41.417 if eqi is None:
2025-07-02 05:51:41.429 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:41.438 atags = btags = ""
2025-07-02 05:51:41.448 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:41.459 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:41.466 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:41.475 if tag == 'replace':
2025-07-02 05:51:41.485 atags += '^' * la
2025-07-02 05:51:41.498 btags += '^' * lb
2025-07-02 05:51:41.507 elif tag == 'delete':
2025-07-02 05:51:41.517 atags += '-' * la
2025-07-02 05:51:41.526 elif tag == 'insert':
2025-07-02 05:51:41.536 btags += '+' * lb
2025-07-02 05:51:41.545 elif tag == 'equal':
2025-07-02 05:51:41.553 atags += ' ' * la
2025-07-02 05:51:41.564 btags += ' ' * lb
2025-07-02 05:51:41.575 else:
2025-07-02 05:51:41.584 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:41.591 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:41.600 else:
2025-07-02 05:51:41.607 # the synch pair is identical
2025-07-02 05:51:41.615 yield '  ' + aelt
2025-07-02 05:51:41.627
2025-07-02 05:51:41.639 # pump out diffs from after the synch point
2025-07-02 05:51:41.650 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:41.659
2025-07-02 05:51:41.672 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:41.685 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:41.695
2025-07-02 05:51:41.707 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:41.717 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:41.724 alo = 393, ahi = 1101
2025-07-02 05:51:41.736 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:41.744 blo = 393, bhi = 1101
2025-07-02 05:51:41.752
2025-07-02 05:51:41.758 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:41.767 g = []
2025-07-02 05:51:41.778 if alo < ahi:
2025-07-02 05:51:41.787 if blo < bhi:
2025-07-02 05:51:41.795 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:41.808 else:
2025-07-02 05:51:41.818 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:41.826 elif blo < bhi:
2025-07-02 05:51:41.836 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:41.849
2025-07-02 05:51:41.862 >       yield from g
2025-07-02 05:51:41.873
2025-07-02 05:51:41.882 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:41.894 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:41.907
2025-07-02 05:51:41.919 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:41.931 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:41.941 alo = 393, ahi = 1101
2025-07-02 05:51:41.953 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:41.962 blo = 393, bhi = 1101
2025-07-02 05:51:41.970
2025-07-02 05:51:41.979 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:41.992 r"""
2025-07-02 05:51:42.004 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:42.016 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:42.026 synch point, and intraline difference marking is done on the
2025-07-02 05:51:42.037 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:42.050
2025-07-02 05:51:42.061 Example:
2025-07-02 05:51:42.069
2025-07-02 05:51:42.085 >>> d = Differ()
2025-07-02 05:51:42.099 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:42.110 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:42.120 >>> print(''.join(results), end="")
2025-07-02 05:51:42.133 - abcDefghiJkl
2025-07-02 05:51:42.150 + abcdefGhijkl
2025-07-02 05:51:42.164 """
2025-07-02 05:51:42.171
2025-07-02 05:51:42.177 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:42.189 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:42.199 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:42.207 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:42.215 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:42.225
2025-07-02 05:51:42.233 # search for the pair that matches best without being identical
2025-07-02 05:51:42.240 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:42.247 # on junk -- unless we have to)
2025-07-02 05:51:42.253 for j in range(blo, bhi):
2025-07-02 05:51:42.259 bj = b[j]
2025-07-02 05:51:42.267 cruncher.set_seq2(bj)
2025-07-02 05:51:42.276 for i in range(alo, ahi):
2025-07-02 05:51:42.284 ai = a[i]
2025-07-02 05:51:42.292 if ai == bj:
2025-07-02 05:51:42.299 if eqi is None:
2025-07-02 05:51:42.306 eqi, eqj = i, j
2025-07-02 05:51:42.319 continue
2025-07-02 05:51:42.330 cruncher.set_seq1(ai)
2025-07-02 05:51:42.337 # computing similarity is expensive, so use the quick
2025-07-02 05:51:42.344 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:42.349 # compares by a factor of 3.
2025-07-02 05:51:42.355 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:42.361 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:42.376 # of the computation is cached by cruncher
2025-07-02 05:51:42.386 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:42.396 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:42.406 cruncher.ratio() > best_ratio:
2025-07-02 05:51:42.414 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:42.420 if best_ratio < cutoff:
2025-07-02 05:51:42.427 # no non-identical "pretty close" pair
2025-07-02 05:51:42.433 if eqi is None:
2025-07-02 05:51:42.437 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:42.442 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:42.447 return
2025-07-02 05:51:42.452 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:42.458 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:42.477 else:
2025-07-02 05:51:42.488 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:42.498 eqi = None
2025-07-02 05:51:42.508
2025-07-02 05:51:42.517 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:42.524 # identical
2025-07-02 05:51:42.530
2025-07-02 05:51:42.539 # pump out diffs from before the synch point
2025-07-02 05:51:42.551 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:42.559
2025-07-02 05:51:42.567 # do intraline marking on the synch pair
2025-07-02 05:51:42.577 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:42.588 if eqi is None:
2025-07-02 05:51:42.598 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:42.606 atags = btags = ""
2025-07-02 05:51:42.616 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:42.632 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:42.641 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:42.648 if tag == 'replace':
2025-07-02 05:51:42.653 atags += '^' * la
2025-07-02 05:51:42.658 btags += '^' * lb
2025-07-02 05:51:42.663 elif tag == 'delete':
2025-07-02 05:51:42.687 atags += '-' * la
2025-07-02 05:51:42.696 elif tag == 'insert':
2025-07-02 05:51:42.705 btags += '+' * lb
2025-07-02 05:51:42.713 elif tag == 'equal':
2025-07-02 05:51:42.723 atags += ' ' * la
2025-07-02 05:51:42.736 btags += ' ' * lb
2025-07-02 05:51:42.746 else:
2025-07-02 05:51:42.754 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:42.762 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:42.768 else:
2025-07-02 05:51:42.778 # the synch pair is identical
2025-07-02 05:51:42.791 yield '  ' + aelt
2025-07-02 05:51:42.801
2025-07-02 05:51:42.809 # pump out diffs from after the synch point
2025-07-02 05:51:42.816 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:42.825
2025-07-02 05:51:42.838 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:42.848 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:42.860
2025-07-02 05:51:42.868 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:42.882 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:42.891 alo = 394, ahi = 1101
2025-07-02 05:51:42.901 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:42.908 blo = 394, bhi = 1101
2025-07-02 05:51:42.914
2025-07-02 05:51:42.921 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:42.927 g = []
2025-07-02 05:51:42.933 if alo < ahi:
2025-07-02 05:51:42.939 if blo < bhi:
2025-07-02 05:51:42.945 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:42.951 else:
2025-07-02 05:51:42.957 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:42.962 elif blo < bhi:
2025-07-02 05:51:42.969 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:42.974
2025-07-02 05:51:42.980 >       yield from g
2025-07-02 05:51:42.987
2025-07-02 05:51:42.995 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:43.007 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:43.016
2025-07-02 05:51:43.025 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:43.037 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:43.047 alo = 394, ahi = 1101
2025-07-02 05:51:43.055 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:43.062 blo = 394, bhi = 1101
2025-07-02 05:51:43.068
2025-07-02 05:51:43.075 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:43.082 r"""
2025-07-02 05:51:43.094 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:43.105 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:43.113 synch point, and intraline difference marking is done on the
2025-07-02 05:51:43.120 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:43.125
2025-07-02 05:51:43.131 Example:
2025-07-02 05:51:43.137
2025-07-02 05:51:43.142 >>> d = Differ()
2025-07-02 05:51:43.153 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:43.162 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:43.170 >>> print(''.join(results), end="")
2025-07-02 05:51:43.179 - abcDefghiJkl
2025-07-02 05:51:43.199 + abcdefGhijkl
2025-07-02 05:51:43.215 """
2025-07-02 05:51:43.223
2025-07-02 05:51:43.231 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:43.238 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:43.250 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:43.260 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:43.272 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:43.281
2025-07-02 05:51:43.289 # search for the pair that matches best without being identical
2025-07-02 05:51:43.295 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:43.309 # on junk -- unless we have to)
2025-07-02 05:51:43.320 for j in range(blo, bhi):
2025-07-02 05:51:43.330 bj = b[j]
2025-07-02 05:51:43.338 cruncher.set_seq2(bj)
2025-07-02 05:51:43.345 for i in range(alo, ahi):
2025-07-02 05:51:43.350 ai = a[i]
2025-07-02 05:51:43.356 if ai == bj:
2025-07-02 05:51:43.362 if eqi is None:
2025-07-02 05:51:43.373 eqi, eqj = i, j
2025-07-02 05:51:43.383 continue
2025-07-02 05:51:43.390 cruncher.set_seq1(ai)
2025-07-02 05:51:43.397 # computing similarity is expensive, so use the quick
2025-07-02 05:51:43.403 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:43.411 # compares by a factor of 3.
2025-07-02 05:51:43.423 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:43.431 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:43.438 # of the computation is cached by cruncher
2025-07-02 05:51:43.445 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:43.451 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:43.458 cruncher.ratio() > best_ratio:
2025-07-02 05:51:43.468 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:43.478 if best_ratio < cutoff:
2025-07-02 05:51:43.485 # no non-identical "pretty close" pair
2025-07-02 05:51:43.494 if eqi is None:
2025-07-02 05:51:43.502 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:43.515 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:43.524 return
2025-07-02 05:51:43.532 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:43.539 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:43.546 else:
2025-07-02 05:51:43.553 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:43.559 eqi = None
2025-07-02 05:51:43.569
2025-07-02 05:51:43.579 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:43.587 # identical
2025-07-02 05:51:43.594
2025-07-02 05:51:43.604 # pump out diffs from before the synch point
2025-07-02 05:51:43.613 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:43.621
2025-07-02 05:51:43.627 # do intraline marking on the synch pair
2025-07-02 05:51:43.636 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:43.648 if eqi is None:
2025-07-02 05:51:43.655 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:43.661 atags = btags = ""
2025-07-02 05:51:43.667 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:43.674 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:43.680 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:43.686 if tag == 'replace':
2025-07-02 05:51:43.691 atags += '^' * la
2025-07-02 05:51:43.696 btags += '^' * lb
2025-07-02 05:51:43.701 elif tag == 'delete':
2025-07-02 05:51:43.710 atags += '-' * la
2025-07-02 05:51:43.722 elif tag == 'insert':
2025-07-02 05:51:43.732 btags += '+' * lb
2025-07-02 05:51:43.739 elif tag == 'equal':
2025-07-02 05:51:43.746 atags += ' ' * la
2025-07-02 05:51:43.752 btags += ' ' * lb
2025-07-02 05:51:43.765 else:
2025-07-02 05:51:43.776 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:43.786 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:43.794 else:
2025-07-02 05:51:43.805 # the synch pair is identical
2025-07-02 05:51:43.815 yield '  ' + aelt
2025-07-02 05:51:43.823
2025-07-02 05:51:43.830 # pump out diffs from after the synch point
2025-07-02 05:51:43.838 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:43.845
2025-07-02 05:51:43.852 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:43.859 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:43.867
2025-07-02 05:51:43.878 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:43.886 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:43.893 alo = 395, ahi = 1101
2025-07-02 05:51:43.905 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:43.916 blo = 395, bhi = 1101
2025-07-02 05:51:43.924
2025-07-02 05:51:43.932 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:43.938 g = []
2025-07-02 05:51:43.944 if alo < ahi:
2025-07-02 05:51:43.950 if blo < bhi:
2025-07-02 05:51:43.960 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:43.970 else:
2025-07-02 05:51:43.978 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:43.987 elif blo < bhi:
2025-07-02 05:51:43.996 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:44.003
2025-07-02 05:51:44.010 >       yield from g
2025-07-02 05:51:44.016
2025-07-02 05:51:44.022 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:44.028 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:44.034
2025-07-02 05:51:44.040 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:44.047 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:44.055 alo = 395, ahi = 1101
2025-07-02 05:51:44.065 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:44.074 blo = 395, bhi = 1101
2025-07-02 05:51:44.080
2025-07-02 05:51:44.086 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:44.097 r"""
2025-07-02 05:51:44.106 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:44.114 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:44.124 synch point, and intraline difference marking is done on the
2025-07-02 05:51:44.136 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:44.147
2025-07-02 05:51:44.158 Example:
2025-07-02 05:51:44.167
2025-07-02 05:51:44.175 >>> d = Differ()
2025-07-02 05:51:44.186 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:44.194 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:44.201 >>> print(''.join(results), end="")
2025-07-02 05:51:44.207 - abcDefghiJkl
2025-07-02 05:51:44.218 + abcdefGhijkl
2025-07-02 05:51:44.230 """
2025-07-02 05:51:44.241
2025-07-02 05:51:44.250 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:44.258 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:44.268 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:44.279 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:44.288 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:44.296
2025-07-02 05:51:44.303 # search for the pair that matches best without being identical
2025-07-02 05:51:44.309 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:44.315 # on junk -- unless we have to)
2025-07-02 05:51:44.320 for j in range(blo, bhi):
2025-07-02 05:51:44.328 bj = b[j]
2025-07-02 05:51:44.337 cruncher.set_seq2(bj)
2025-07-02 05:51:44.345 for i in range(alo, ahi):
2025-07-02 05:51:44.351 ai = a[i]
2025-07-02 05:51:44.358 if ai == bj:
2025-07-02 05:51:44.365 if eqi is None:
2025-07-02 05:51:44.371 eqi, eqj = i, j
2025-07-02 05:51:44.379 continue
2025-07-02 05:51:44.386 cruncher.set_seq1(ai)
2025-07-02 05:51:44.395 # computing similarity is expensive, so use the quick
2025-07-02 05:51:44.407 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:44.415 # compares by a factor of 3.
2025-07-02 05:51:44.423 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:44.430 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:44.440 # of the computation is cached by cruncher
2025-07-02 05:51:44.452 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:44.463 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:44.474 cruncher.ratio() > best_ratio:
2025-07-02 05:51:44.485 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:44.492 if best_ratio < cutoff:
2025-07-02 05:51:44.498 # no non-identical "pretty close" pair
2025-07-02 05:51:44.503 if eqi is None:
2025-07-02 05:51:44.508 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:44.513 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:44.518 return
2025-07-02 05:51:44.524 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:44.531 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:44.539 else:
2025-07-02 05:51:44.552 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:44.558 eqi = None
2025-07-02 05:51:44.567
2025-07-02 05:51:44.577 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:44.586 # identical
2025-07-02 05:51:44.592
2025-07-02 05:51:44.597 # pump out diffs from before the synch point
2025-07-02 05:51:44.603 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:44.613
2025-07-02 05:51:44.624 # do intraline marking on the synch pair
2025-07-02 05:51:44.636 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:44.646 if eqi is None:
2025-07-02 05:51:44.654 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:44.660 atags = btags = ""
2025-07-02 05:51:44.666 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:44.679 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:44.689 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:44.697 if tag == 'replace':
2025-07-02 05:51:44.705 atags += '^' * la
2025-07-02 05:51:44.712 btags += '^' * lb
2025-07-02 05:51:44.717 elif tag == 'delete':
2025-07-02 05:51:44.723 atags += '-' * la
2025-07-02 05:51:44.733 elif tag == 'insert':
2025-07-02 05:51:44.745 btags += '+' * lb
2025-07-02 05:51:44.755 elif tag == 'equal':
2025-07-02 05:51:44.764 atags += ' ' * la
2025-07-02 05:51:44.772 btags += ' ' * lb
2025-07-02 05:51:44.779 else:
2025-07-02 05:51:44.788 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:44.795 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:44.803 else:
2025-07-02 05:51:44.812 # the synch pair is identical
2025-07-02 05:51:44.824 yield '  ' + aelt
2025-07-02 05:51:44.834
2025-07-02 05:51:44.841 # pump out diffs from after the synch point
2025-07-02 05:51:44.848 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:44.854
2025-07-02 05:51:44.864 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:44.875 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:44.883
2025-07-02 05:51:44.890 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:44.899 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:44.910 alo = 396, ahi = 1101
2025-07-02 05:51:44.923 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:44.931 blo = 396, bhi = 1101
2025-07-02 05:51:44.943
2025-07-02 05:51:44.953 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:44.961 g = []
2025-07-02 05:51:44.968 if alo < ahi:
2025-07-02 05:51:44.981 if blo < bhi:
2025-07-02 05:51:44.994 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:45.005 else:
2025-07-02 05:51:45.017 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:45.027 elif blo < bhi:
2025-07-02 05:51:45.040 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:45.053
2025-07-02 05:51:45.062 >       yield from g
2025-07-02 05:51:45.075
2025-07-02 05:51:45.085 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:45.097 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:45.109
2025-07-02 05:51:45.120 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:45.130 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:45.144 alo = 396, ahi = 1101
2025-07-02 05:51:45.154 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:45.163 blo = 396, bhi = 1101
2025-07-02 05:51:45.170
2025-07-02 05:51:45.178 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:45.188 r"""
2025-07-02 05:51:45.200 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:45.209 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:45.217 synch point, and intraline difference marking is done on the
2025-07-02 05:51:45.225 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:45.232
2025-07-02 05:51:45.239 Example:
2025-07-02 05:51:45.249
2025-07-02 05:51:45.258 >>> d = Differ()
2025-07-02 05:51:45.266 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:45.274 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:45.281 >>> print(''.join(results), end="")
2025-07-02 05:51:45.288 - abcDefghiJkl
2025-07-02 05:51:45.299 + abcdefGhijkl
2025-07-02 05:51:45.312 """
2025-07-02 05:51:45.318
2025-07-02 05:51:45.325 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:45.331 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:45.336 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:45.342 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:45.359 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:45.366
2025-07-02 05:51:45.372 # search for the pair that matches best without being identical
2025-07-02 05:51:45.379 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:45.384 # on junk -- unless we have to)
2025-07-02 05:51:45.390 for j in range(blo, bhi):
2025-07-02 05:51:45.400 bj = b[j]
2025-07-02 05:51:45.405 cruncher.set_seq2(bj)
2025-07-02 05:51:45.412 for i in range(alo, ahi):
2025-07-02 05:51:45.418 ai = a[i]
2025-07-02 05:51:45.425 if ai == bj:
2025-07-02 05:51:45.432 if eqi is None:
2025-07-02 05:51:45.440 eqi, eqj = i, j
2025-07-02 05:51:45.447 continue
2025-07-02 05:51:45.454 cruncher.set_seq1(ai)
2025-07-02 05:51:45.462 # computing similarity is expensive, so use the quick
2025-07-02 05:51:45.470 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:45.482 # compares by a factor of 3.
2025-07-02 05:51:45.496 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:45.508 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:45.516 # of the computation is cached by cruncher
2025-07-02 05:51:45.524 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:45.530 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:45.537 cruncher.ratio() > best_ratio:
2025-07-02 05:51:45.547 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:45.556 if best_ratio < cutoff:
2025-07-02 05:51:45.564 # no non-identical "pretty close" pair
2025-07-02 05:51:45.570 if eqi is None:
2025-07-02 05:51:45.581 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:45.592 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:45.601 return
2025-07-02 05:51:45.614 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:45.626 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:45.633 else:
2025-07-02 05:51:45.640 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:45.647 eqi = None
2025-07-02 05:51:45.656
2025-07-02 05:51:45.663 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:45.671 # identical
2025-07-02 05:51:45.683
2025-07-02 05:51:45.692 # pump out diffs from before the synch point
2025-07-02 05:51:45.707 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:45.719
2025-07-02 05:51:45.730 # do intraline marking on the synch pair
2025-07-02 05:51:45.739 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:45.748 if eqi is None:
2025-07-02 05:51:45.755 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:45.764 atags = btags = ""
2025-07-02 05:51:45.775 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:45.783 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:45.796 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:45.809 if tag == 'replace':
2025-07-02 05:51:45.819 atags += '^' * la
2025-07-02 05:51:45.831 btags += '^' * lb
2025-07-02 05:51:45.842 elif tag == 'delete':
2025-07-02 05:51:45.851 atags += '-' * la
2025-07-02 05:51:45.863 elif tag == 'insert':
2025-07-02 05:51:45.872 btags += '+' * lb
2025-07-02 05:51:45.880 elif tag == 'equal':
2025-07-02 05:51:45.888 atags += ' ' * la
2025-07-02 05:51:45.897 btags += ' ' * lb
2025-07-02 05:51:45.909 else:
2025-07-02 05:51:45.918 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:45.931 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:45.943 else:
2025-07-02 05:51:45.953 # the synch pair is identical
2025-07-02 05:51:45.961 yield '  ' + aelt
2025-07-02 05:51:45.968
2025-07-02 05:51:45.975 # pump out diffs from after the synch point
2025-07-02 05:51:45.981 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:45.993
2025-07-02 05:51:46.003 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:46.010 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:46.016
2025-07-02 05:51:46.021 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:46.026 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:46.031 alo = 397, ahi = 1101
2025-07-02 05:51:46.036 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:46.041 blo = 397, bhi = 1101
2025-07-02 05:51:46.046
2025-07-02 05:51:46.050 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:46.055 g = []
2025-07-02 05:51:46.060 if alo < ahi:
2025-07-02 05:51:46.064 if blo < bhi:
2025-07-02 05:51:46.070 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:46.077 else:
2025-07-02 05:51:46.084 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:46.090 elif blo < bhi:
2025-07-02 05:51:46.097 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:46.103
2025-07-02 05:51:46.110 >       yield from g
2025-07-02 05:51:46.116
2025-07-02 05:51:46.123 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:46.130 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:46.136
2025-07-02 05:51:46.143 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:46.151 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:46.163 alo = 397, ahi = 1101
2025-07-02 05:51:46.172 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:46.180 blo = 397, bhi = 1101
2025-07-02 05:51:46.194
2025-07-02 05:51:46.208 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:46.217 r"""
2025-07-02 05:51:46.227 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:46.238 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:46.248 synch point, and intraline difference marking is done on the
2025-07-02 05:51:46.261 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:46.270
2025-07-02 05:51:46.278 Example:
2025-07-02 05:51:46.286
2025-07-02 05:51:46.295 >>> d = Differ()
2025-07-02 05:51:46.301 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:46.306 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:46.311 >>> print(''.join(results), end="")
2025-07-02 05:51:46.318 - abcDefghiJkl
2025-07-02 05:51:46.334 + abcdefGhijkl
2025-07-02 05:51:46.360 """
2025-07-02 05:51:46.370
2025-07-02 05:51:46.377 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:46.384 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:46.390 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:46.397 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:46.408 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:46.416
2025-07-02 05:51:46.423 # search for the pair that matches best without being identical
2025-07-02 05:51:46.432 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:46.440 # on junk -- unless we have to)
2025-07-02 05:51:46.448 for j in range(blo, bhi):
2025-07-02 05:51:46.455 bj = b[j]
2025-07-02 05:51:46.463 cruncher.set_seq2(bj)
2025-07-02 05:51:46.470 for i in range(alo, ahi):
2025-07-02 05:51:46.481 ai = a[i]
2025-07-02 05:51:46.494 if ai == bj:
2025-07-02 05:51:46.509 if eqi is None:
2025-07-02 05:51:46.523 eqi, eqj = i, j
2025-07-02 05:51:46.533 continue
2025-07-02 05:51:46.543 cruncher.set_seq1(ai)
2025-07-02 05:51:46.556 # computing similarity is expensive, so use the quick
2025-07-02 05:51:46.564 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:46.572 # compares by a factor of 3.
2025-07-02 05:51:46.578 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:46.586 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:46.594 # of the computation is cached by cruncher
2025-07-02 05:51:46.601 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:46.609 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:46.621 cruncher.ratio() > best_ratio:
2025-07-02 05:51:46.629 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:46.635 if best_ratio < cutoff:
2025-07-02 05:51:46.642 # no non-identical "pretty close" pair
2025-07-02 05:51:46.651 if eqi is None:
2025-07-02 05:51:46.660 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:46.669 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:46.676 return
2025-07-02 05:51:46.684 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:46.692 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:46.699 else:
2025-07-02 05:51:46.707 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:46.715 eqi = None
2025-07-02 05:51:46.727
2025-07-02 05:51:46.736 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:46.743 # identical
2025-07-02 05:51:46.751
2025-07-02 05:51:46.761 # pump out diffs from before the synch point
2025-07-02 05:51:46.769 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:46.777
2025-07-02 05:51:46.783 # do intraline marking on the synch pair
2025-07-02 05:51:46.791 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:46.801 if eqi is None:
2025-07-02 05:51:46.815 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:46.829 atags = btags = ""
2025-07-02 05:51:46.839 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:46.847 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:46.854 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:46.867 if tag == 'replace':
2025-07-02 05:51:46.878 atags += '^' * la
2025-07-02 05:51:46.887 btags += '^' * lb
2025-07-02 05:51:46.898 elif tag == 'delete':
2025-07-02 05:51:46.906 atags += '-' * la
2025-07-02 05:51:46.913 elif tag == 'insert':
2025-07-02 05:51:46.921 btags += '+' * lb
2025-07-02 05:51:46.926 elif tag == 'equal':
2025-07-02 05:51:46.932 atags += ' ' * la
2025-07-02 05:51:46.941 btags += ' ' * lb
2025-07-02 05:51:46.953 else:
2025-07-02 05:51:46.964 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:46.972 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:46.978 else:
2025-07-02 05:51:46.983 # the synch pair is identical
2025-07-02 05:51:46.991 yield '  ' + aelt
2025-07-02 05:51:47.000
2025-07-02 05:51:47.013 # pump out diffs from after the synch point
2025-07-02 05:51:47.023 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:47.036
2025-07-02 05:51:47.046 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:47.057 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:47.070
2025-07-02 05:51:47.084 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:47.097 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:47.110 alo = 400, ahi = 1101
2025-07-02 05:51:47.123 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:47.137 blo = 400, bhi = 1101
2025-07-02 05:51:47.147
2025-07-02 05:51:47.154 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:47.169 g = []
2025-07-02 05:51:47.179 if alo < ahi:
2025-07-02 05:51:47.188 if blo < bhi:
2025-07-02 05:51:47.197 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:47.209 else:
2025-07-02 05:51:47.217 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:47.225 elif blo < bhi:
2025-07-02 05:51:47.232 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:47.239
2025-07-02 05:51:47.245 >       yield from g
2025-07-02 05:51:47.254
2025-07-02 05:51:47.265 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:47.273 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:47.279
2025-07-02 05:51:47.291 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:47.301 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:47.309 alo = 400, ahi = 1101
2025-07-02 05:51:47.317 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:47.328 blo = 400, bhi = 1101
2025-07-02 05:51:47.340
2025-07-02 05:51:47.349 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:47.357 r"""
2025-07-02 05:51:47.365 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:47.374 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:47.387 synch point, and intraline difference marking is done on the
2025-07-02 05:51:47.397 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:47.407
2025-07-02 05:51:47.418 Example:
2025-07-02 05:51:47.426
2025-07-02 05:51:47.432 >>> d = Differ()
2025-07-02 05:51:47.439 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:47.446 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:47.455 >>> print(''.join(results), end="")
2025-07-02 05:51:47.465 - abcDefghiJkl
2025-07-02 05:51:47.489 + abcdefGhijkl
2025-07-02 05:51:47.508 """
2025-07-02 05:51:47.519
2025-07-02 05:51:47.532 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:47.544 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:47.555 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:47.563 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:47.571 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:47.577
2025-07-02 05:51:47.583 # search for the pair that matches best without being identical
2025-07-02 05:51:47.590 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:47.597 # on junk -- unless we have to)
2025-07-02 05:51:47.603 for j in range(blo, bhi):
2025-07-02 05:51:47.608 bj = b[j]
2025-07-02 05:51:47.615 cruncher.set_seq2(bj)
2025-07-02 05:51:47.622 for i in range(alo, ahi):
2025-07-02 05:51:47.635 ai = a[i]
2025-07-02 05:51:47.648 if ai == bj:
2025-07-02 05:51:47.659 if eqi is None:
2025-07-02 05:51:47.666 eqi, eqj = i, j
2025-07-02 05:51:47.672 continue
2025-07-02 05:51:47.677 cruncher.set_seq1(ai)
2025-07-02 05:51:47.684 # computing similarity is expensive, so use the quick
2025-07-02 05:51:47.691 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:47.697 # compares by a factor of 3.
2025-07-02 05:51:47.705 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:47.711 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:47.716 # of the computation is cached by cruncher
2025-07-02 05:51:47.722 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:47.736 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:47.748 cruncher.ratio() > best_ratio:
2025-07-02 05:51:47.757 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:47.765 if best_ratio < cutoff:
2025-07-02 05:51:47.773 # no non-identical "pretty close" pair
2025-07-02 05:51:47.781 if eqi is None:
2025-07-02 05:51:47.795 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:47.803 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:47.811 return
2025-07-02 05:51:47.818 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:47.826 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:47.832 else:
2025-07-02 05:51:47.839 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:47.845 eqi = None
2025-07-02 05:51:47.851
2025-07-02 05:51:47.859 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:47.871 # identical
2025-07-02 05:51:47.879
2025-07-02 05:51:47.887 # pump out diffs from before the synch point
2025-07-02 05:51:47.896 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:47.908
2025-07-02 05:51:47.920 # do intraline marking on the synch pair
2025-07-02 05:51:47.931 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:47.941 if eqi is None:
2025-07-02 05:51:47.949 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:47.960 atags = btags = ""
2025-07-02 05:51:47.967 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:47.974 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:47.980 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:47.986 if tag == 'replace':
2025-07-02 05:51:47.992 atags += '^' * la
2025-07-02 05:51:47.998 btags += '^' * lb
2025-07-02 05:51:48.004 elif tag == 'delete':
2025-07-02 05:51:48.011 atags += '-' * la
2025-07-02 05:51:48.018 elif tag == 'insert':
2025-07-02 05:51:48.030 btags += '+' * lb
2025-07-02 05:51:48.037 elif tag == 'equal':
2025-07-02 05:51:48.044 atags += ' ' * la
2025-07-02 05:51:48.051 btags += ' ' * lb
2025-07-02 05:51:48.058 else:
2025-07-02 05:51:48.065 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:48.071 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:48.078 else:
2025-07-02 05:51:48.084 # the synch pair is identical
2025-07-02 05:51:48.091 yield '  ' + aelt
2025-07-02 05:51:48.098
2025-07-02 05:51:48.105 # pump out diffs from after the synch point
2025-07-02 05:51:48.112 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:48.118
2025-07-02 05:51:48.125 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:48.132 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:48.139
2025-07-02 05:51:48.147 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:48.159 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:48.171 alo = 401, ahi = 1101
2025-07-02 05:51:48.184 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:48.190 blo = 401, bhi = 1101
2025-07-02 05:51:48.201
2025-07-02 05:51:48.210 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:48.218 g = []
2025-07-02 05:51:48.228 if alo < ahi:
2025-07-02 05:51:48.234 if blo < bhi:
2025-07-02 05:51:48.241 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:48.248 else:
2025-07-02 05:51:48.255 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:48.262 elif blo < bhi:
2025-07-02 05:51:48.268 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:48.275
2025-07-02 05:51:48.282 >       yield from g
2025-07-02 05:51:48.289
2025-07-02 05:51:48.296 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:48.303 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:48.311
2025-07-02 05:51:48.322 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:48.335 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:48.344 alo = 401, ahi = 1101
2025-07-02 05:51:48.358 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:48.368 blo = 401, bhi = 1101
2025-07-02 05:51:48.375
2025-07-02 05:51:48.382 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:48.389 r"""
2025-07-02 05:51:48.397 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:48.404 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:48.411 synch point, and intraline difference marking is done on the
2025-07-02 05:51:48.418 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:48.424
2025-07-02 05:51:48.431 Example:
2025-07-02 05:51:48.437
2025-07-02 05:51:48.446 >>> d = Differ()
2025-07-02 05:51:48.455 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:48.467 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:48.478 >>> print(''.join(results), end="")
2025-07-02 05:51:48.487 - abcDefghiJkl
2025-07-02 05:51:48.509 + abcdefGhijkl
2025-07-02 05:51:48.528 """
2025-07-02 05:51:48.542
2025-07-02 05:51:48.549 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:48.556 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:48.562 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:48.569 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:48.582 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:48.593
2025-07-02 05:51:48.601 # search for the pair that matches best without being identical
2025-07-02 05:51:48.611 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:48.622 # on junk -- unless we have to)
2025-07-02 05:51:48.630 for j in range(blo, bhi):
2025-07-02 05:51:48.637 bj = b[j]
2025-07-02 05:51:48.642 cruncher.set_seq2(bj)
2025-07-02 05:51:48.648 for i in range(alo, ahi):
2025-07-02 05:51:48.654 ai = a[i]
2025-07-02 05:51:48.660 if ai == bj:
2025-07-02 05:51:48.666 if eqi is None:
2025-07-02 05:51:48.673 eqi, eqj = i, j
2025-07-02 05:51:48.679 continue
2025-07-02 05:51:48.687 cruncher.set_seq1(ai)
2025-07-02 05:51:48.699 # computing similarity is expensive, so use the quick
2025-07-02 05:51:48.708 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:48.716 # compares by a factor of 3.
2025-07-02 05:51:48.722 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:48.731 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:48.744 # of the computation is cached by cruncher
2025-07-02 05:51:48.754 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:48.762 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:48.771 cruncher.ratio() > best_ratio:
2025-07-02 05:51:48.783 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:48.790 if best_ratio < cutoff:
2025-07-02 05:51:48.797 # no non-identical "pretty close" pair
2025-07-02 05:51:48.802 if eqi is None:
2025-07-02 05:51:48.809 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:48.814 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:48.821 return
2025-07-02 05:51:48.832 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:48.844 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:48.858 else:
2025-07-02 05:51:48.869 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:48.879 eqi = None
2025-07-02 05:51:48.892
2025-07-02 05:51:48.902 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:48.914 # identical
2025-07-02 05:51:48.927
2025-07-02 05:51:48.938 # pump out diffs from before the synch point
2025-07-02 05:51:48.950 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:48.959
2025-07-02 05:51:48.965 # do intraline marking on the synch pair
2025-07-02 05:51:48.971 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:48.976 if eqi is None:
2025-07-02 05:51:48.984 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:48.993 atags = btags = ""
2025-07-02 05:51:49.000 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:49.010 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:49.016 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:49.022 if tag == 'replace':
2025-07-02 05:51:49.028 atags += '^' * la
2025-07-02 05:51:49.035 btags += '^' * lb
2025-07-02 05:51:49.043 elif tag == 'delete':
2025-07-02 05:51:49.053 atags += '-' * la
2025-07-02 05:51:49.062 elif tag == 'insert':
2025-07-02 05:51:49.070 btags += '+' * lb
2025-07-02 05:51:49.075 elif tag == 'equal':
2025-07-02 05:51:49.081 atags += ' ' * la
2025-07-02 05:51:49.086 btags += ' ' * lb
2025-07-02 05:51:49.094 else:
2025-07-02 05:51:49.104 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:49.112 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:49.119 else:
2025-07-02 05:51:49.128 # the synch pair is identical
2025-07-02 05:51:49.134 yield '  ' + aelt
2025-07-02 05:51:49.139
2025-07-02 05:51:49.145 # pump out diffs from after the synch point
2025-07-02 05:51:49.152 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:49.158
2025-07-02 05:51:49.165 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:49.172 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:49.178
2025-07-02 05:51:49.189 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:49.198 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:49.211 alo = 402, ahi = 1101
2025-07-02 05:51:49.221 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:49.228 blo = 402, bhi = 1101
2025-07-02 05:51:49.234
2025-07-02 05:51:49.240 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:49.246 g = []
2025-07-02 05:51:49.257 if alo < ahi:
2025-07-02 05:51:49.267 if blo < bhi:
2025-07-02 05:51:49.275 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:49.285 else:
2025-07-02 05:51:49.299 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:49.310 elif blo < bhi:
2025-07-02 05:51:49.320 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:49.331
2025-07-02 05:51:49.342 >       yield from g
2025-07-02 05:51:49.354
2025-07-02 05:51:49.363 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:49.377 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:49.390
2025-07-02 05:51:49.402 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:49.416 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:49.427 alo = 402, ahi = 1101
2025-07-02 05:51:49.439 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:49.448 blo = 402, bhi = 1101
2025-07-02 05:51:49.456
2025-07-02 05:51:49.463 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:49.470 r"""
2025-07-02 05:51:49.479 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:49.487 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:49.493 synch point, and intraline difference marking is done on the
2025-07-02 05:51:49.499 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:49.508
2025-07-02 05:51:49.518 Example:
2025-07-02 05:51:49.526
2025-07-02 05:51:49.537 >>> d = Differ()
2025-07-02 05:51:49.547 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:49.557 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:49.565 >>> print(''.join(results), end="")
2025-07-02 05:51:49.572 - abcDefghiJkl
2025-07-02 05:51:49.587 + abcdefGhijkl
2025-07-02 05:51:49.599 """
2025-07-02 05:51:49.604
2025-07-02 05:51:49.609 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:49.615 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:49.621 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:49.633 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:49.645 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:49.656
2025-07-02 05:51:49.665 # search for the pair that matches best without being identical
2025-07-02 05:51:49.672 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:49.679 # on junk -- unless we have to)
2025-07-02 05:51:49.687 for j in range(blo, bhi):
2025-07-02 05:51:49.696 bj = b[j]
2025-07-02 05:51:49.706 cruncher.set_seq2(bj)
2025-07-02 05:51:49.717 for i in range(alo, ahi):
2025-07-02 05:51:49.728 ai = a[i]
2025-07-02 05:51:49.735 if ai == bj:
2025-07-02 05:51:49.743 if eqi is None:
2025-07-02 05:51:49.750 eqi, eqj = i, j
2025-07-02 05:51:49.761 continue
2025-07-02 05:51:49.771 cruncher.set_seq1(ai)
2025-07-02 05:51:49.779 # computing similarity is expensive, so use the quick
2025-07-02 05:51:49.786 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:49.799 # compares by a factor of 3.
2025-07-02 05:51:49.810 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:49.816 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:49.822 # of the computation is cached by cruncher
2025-07-02 05:51:49.833 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:49.843 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:49.852 cruncher.ratio() > best_ratio:
2025-07-02 05:51:49.860 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:49.866 if best_ratio < cutoff:
2025-07-02 05:51:49.873 # no non-identical "pretty close" pair
2025-07-02 05:51:49.879 if eqi is None:
2025-07-02 05:51:49.886 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:49.897 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:49.906 return
2025-07-02 05:51:49.913 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:49.920 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:49.926 else:
2025-07-02 05:51:49.933 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:49.940 eqi = None
2025-07-02 05:51:49.946
2025-07-02 05:51:49.952 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:49.957 # identical
2025-07-02 05:51:49.962
2025-07-02 05:51:49.967 # pump out diffs from before the synch point
2025-07-02 05:51:49.973 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:49.978
2025-07-02 05:51:49.984 # do intraline marking on the synch pair
2025-07-02 05:51:49.990 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:50.001 if eqi is None:
2025-07-02 05:51:50.010 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:50.017 atags = btags = ""
2025-07-02 05:51:50.026 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:50.038 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:50.047 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:50.056 if tag == 'replace':
2025-07-02 05:51:50.064 atags += '^' * la
2025-07-02 05:51:50.070 btags += '^' * lb
2025-07-02 05:51:50.077 elif tag == 'delete':
2025-07-02 05:51:50.082 atags += '-' * la
2025-07-02 05:51:50.088 elif tag == 'insert':
2025-07-02 05:51:50.094 btags += '+' * lb
2025-07-02 05:51:50.100 elif tag == 'equal':
2025-07-02 05:51:50.106 atags += ' ' * la
2025-07-02 05:51:50.113 btags += ' ' * lb
2025-07-02 05:51:50.119 else:
2025-07-02 05:51:50.125 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:50.131 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:50.141 else:
2025-07-02 05:51:50.150 # the synch pair is identical
2025-07-02 05:51:50.159 yield '  ' + aelt
2025-07-02 05:51:50.167
2025-07-02 05:51:50.179 # pump out diffs from after the synch point
2025-07-02 05:51:50.189 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:50.197
2025-07-02 05:51:50.204 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:50.211 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:50.217
2025-07-02 05:51:50.223 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:50.231 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:50.238 alo = 403, ahi = 1101
2025-07-02 05:51:50.243 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:50.249 blo = 403, bhi = 1101
2025-07-02 05:51:50.254
2025-07-02 05:51:50.259 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:50.265 g = []
2025-07-02 05:51:50.271 if alo < ahi:
2025-07-02 05:51:50.283 if blo < bhi:
2025-07-02 05:51:50.294 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:50.305 else:
2025-07-02 05:51:50.316 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:50.324 elif blo < bhi:
2025-07-02 05:51:50.331 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:50.338
2025-07-02 05:51:50.349 >       yield from g
2025-07-02 05:51:50.360
2025-07-02 05:51:50.367 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:50.374 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:50.379
2025-07-02 05:51:50.385 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:50.392 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:50.396 alo = 403, ahi = 1101
2025-07-02 05:51:50.402 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:50.406 blo = 403, bhi = 1101
2025-07-02 05:51:50.411
2025-07-02 05:51:50.416 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:50.422 r"""
2025-07-02 05:51:50.427 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:50.435 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:50.453 synch point, and intraline difference marking is done on the
2025-07-02 05:51:50.467 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:50.480
2025-07-02 05:51:50.492 Example:
2025-07-02 05:51:50.501
2025-07-02 05:51:50.515 >>> d = Differ()
2025-07-02 05:51:50.527 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:50.539 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:50.549 >>> print(''.join(results), end="")
2025-07-02 05:51:50.563 - abcDefghiJkl
2025-07-02 05:51:50.581 + abcdefGhijkl
2025-07-02 05:51:50.594 """
2025-07-02 05:51:50.601
2025-07-02 05:51:50.608 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:50.614 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:50.622 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:50.631 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:50.641 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:50.653
2025-07-02 05:51:50.665 # search for the pair that matches best without being identical
2025-07-02 05:51:50.674 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:50.682 # on junk -- unless we have to)
2025-07-02 05:51:50.696 for j in range(blo, bhi):
2025-07-02 05:51:50.711 bj = b[j]
2025-07-02 05:51:50.720 cruncher.set_seq2(bj)
2025-07-02 05:51:50.728 for i in range(alo, ahi):
2025-07-02 05:51:50.736 ai = a[i]
2025-07-02 05:51:50.743 if ai == bj:
2025-07-02 05:51:50.749 if eqi is None:
2025-07-02 05:51:50.754 eqi, eqj = i, j
2025-07-02 05:51:50.761 continue
2025-07-02 05:51:50.768 cruncher.set_seq1(ai)
2025-07-02 05:51:50.775 # computing similarity is expensive, so use the quick
2025-07-02 05:51:50.782 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:50.789 # compares by a factor of 3.
2025-07-02 05:51:50.795 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:50.802 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:50.810 # of the computation is cached by cruncher
2025-07-02 05:51:50.819 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:50.832 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:50.845 cruncher.ratio() > best_ratio:
2025-07-02 05:51:50.855 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:50.864 if best_ratio < cutoff:
2025-07-02 05:51:50.870 # no non-identical "pretty close" pair
2025-07-02 05:51:50.879 if eqi is None:
2025-07-02 05:51:50.891 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:50.905 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:50.916 return
2025-07-02 05:51:50.924 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:50.931 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:50.940 else:
2025-07-02 05:51:50.948 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:50.954 eqi = None
2025-07-02 05:51:50.962
2025-07-02 05:51:50.974 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:50.986 # identical
2025-07-02 05:51:50.997
2025-07-02 05:51:51.005 # pump out diffs from before the synch point
2025-07-02 05:51:51.012 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:51.018
2025-07-02 05:51:51.032 # do intraline marking on the synch pair
2025-07-02 05:51:51.045 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:51.054 if eqi is None:
2025-07-02 05:51:51.066 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:51.077 atags = btags = ""
2025-07-02 05:51:51.085 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:51.091 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:51.099 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:51.109 if tag == 'replace':
2025-07-02 05:51:51.118 atags += '^' * la
2025-07-02 05:51:51.127 btags += '^' * lb
2025-07-02 05:51:51.136 elif tag == 'delete':
2025-07-02 05:51:51.144 atags += '-' * la
2025-07-02 05:51:51.151 elif tag == 'insert':
2025-07-02 05:51:51.158 btags += '+' * lb
2025-07-02 05:51:51.164 elif tag == 'equal':
2025-07-02 05:51:51.171 atags += ' ' * la
2025-07-02 05:51:51.178 btags += ' ' * lb
2025-07-02 05:51:51.183 else:
2025-07-02 05:51:51.188 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:51.193 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:51.198 else:
2025-07-02 05:51:51.206 # the synch pair is identical
2025-07-02 05:51:51.216 yield '  ' + aelt
2025-07-02 05:51:51.227
2025-07-02 05:51:51.239 # pump out diffs from after the synch point
2025-07-02 05:51:51.252 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:51.262
2025-07-02 05:51:51.271 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:51.284 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:51.295
2025-07-02 05:51:51.302 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:51.309 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:51.317 alo = 404, ahi = 1101
2025-07-02 05:51:51.326 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:51.333 blo = 404, bhi = 1101
2025-07-02 05:51:51.340
2025-07-02 05:51:51.347 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:51.355 g = []
2025-07-02 05:51:51.366 if alo < ahi:
2025-07-02 05:51:51.374 if blo < bhi:
2025-07-02 05:51:51.382 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:51.390 else:
2025-07-02 05:51:51.400 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:51.407 elif blo < bhi:
2025-07-02 05:51:51.414 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:51.427
2025-07-02 05:51:51.438 >       yield from g
2025-07-02 05:51:51.449
2025-07-02 05:51:51.458 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:51.469 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:51.480
2025-07-02 05:51:51.491 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:51.500 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:51.505 alo = 404, ahi = 1101
2025-07-02 05:51:51.511 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:51.517 blo = 404, bhi = 1101
2025-07-02 05:51:51.527
2025-07-02 05:51:51.538 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:51.547 r"""
2025-07-02 05:51:51.560 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:51.568 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:51.576 synch point, and intraline difference marking is done on the
2025-07-02 05:51:51.582 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:51.588
2025-07-02 05:51:51.594 Example:
2025-07-02 05:51:51.599
2025-07-02 05:51:51.607 >>> d = Differ()
2025-07-02 05:51:51.622 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:51.630 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:51.638 >>> print(''.join(results), end="")
2025-07-02 05:51:51.649 - abcDefghiJkl
2025-07-02 05:51:51.664 + abcdefGhijkl
2025-07-02 05:51:51.683 """
2025-07-02 05:51:51.692
2025-07-02 05:51:51.699 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:51.714 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:51.726 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:51.736 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:51.745 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:51.753
2025-07-02 05:51:51.759 # search for the pair that matches best without being identical
2025-07-02 05:51:51.765 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:51.771 # on junk -- unless we have to)
2025-07-02 05:51:51.777 for j in range(blo, bhi):
2025-07-02 05:51:51.790 bj = b[j]
2025-07-02 05:51:51.801 cruncher.set_seq2(bj)
2025-07-02 05:51:51.812 for i in range(alo, ahi):
2025-07-02 05:51:51.825 ai = a[i]
2025-07-02 05:51:51.833 if ai == bj:
2025-07-02 05:51:51.840 if eqi is None:
2025-07-02 05:51:51.847 eqi, eqj = i, j
2025-07-02 05:51:51.856 continue
2025-07-02 05:51:51.868 cruncher.set_seq1(ai)
2025-07-02 05:51:51.878 # computing similarity is expensive, so use the quick
2025-07-02 05:51:51.887 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:51.897 # compares by a factor of 3.
2025-07-02 05:51:51.908 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:51.916 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:51.923 # of the computation is cached by cruncher
2025-07-02 05:51:51.931 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:51.939 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:51.947 cruncher.ratio() > best_ratio:
2025-07-02 05:51:51.957 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:51.964 if best_ratio < cutoff:
2025-07-02 05:51:51.971 # no non-identical "pretty close" pair
2025-07-02 05:51:51.979 if eqi is None:
2025-07-02 05:51:51.986 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:51.994 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:52.002 return
2025-07-02 05:51:52.010 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:52.017 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:52.029 else:
2025-07-02 05:51:52.039 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:52.047 eqi = None
2025-07-02 05:51:52.053
2025-07-02 05:51:52.059 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:52.065 # identical
2025-07-02 05:51:52.073
2025-07-02 05:51:52.080 # pump out diffs from before the synch point
2025-07-02 05:51:52.087 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:52.096
2025-07-02 05:51:52.104 # do intraline marking on the synch pair
2025-07-02 05:51:52.112 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:52.119 if eqi is None:
2025-07-02 05:51:52.126 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:52.132 atags = btags = ""
2025-07-02 05:51:52.139 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:52.146 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:52.157 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:52.169 if tag == 'replace':
2025-07-02 05:51:52.179 atags += '^' * la
2025-07-02 05:51:52.187 btags += '^' * lb
2025-07-02 05:51:52.199 elif tag == 'delete':
2025-07-02 05:51:52.209 atags += '-' * la
2025-07-02 05:51:52.219 elif tag == 'insert':
2025-07-02 05:51:52.230 btags += '+' * lb
2025-07-02 05:51:52.239 elif tag == 'equal':
2025-07-02 05:51:52.247 atags += ' ' * la
2025-07-02 05:51:52.255 btags += ' ' * lb
2025-07-02 05:51:52.268 else:
2025-07-02 05:51:52.276 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:52.285 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:52.292 else:
2025-07-02 05:51:52.298 # the synch pair is identical
2025-07-02 05:51:52.304 yield '  ' + aelt
2025-07-02 05:51:52.310
2025-07-02 05:51:52.321 # pump out diffs from after the synch point
2025-07-02 05:51:52.332 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:52.341
2025-07-02 05:51:52.354 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:52.365 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:52.373
2025-07-02 05:51:52.380 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:52.387 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:52.393 alo = 405, ahi = 1101
2025-07-02 05:51:52.405 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:52.417 blo = 405, bhi = 1101
2025-07-02 05:51:52.428
2025-07-02 05:51:52.441 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:52.449 g = []
2025-07-02 05:51:52.455 if alo < ahi:
2025-07-02 05:51:52.461 if blo < bhi:
2025-07-02 05:51:52.467 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:52.473 else:
2025-07-02 05:51:52.479 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:52.486 elif blo < bhi:
2025-07-02 05:51:52.499 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:52.510
2025-07-02 05:51:52.521 >       yield from g
2025-07-02 05:51:52.533
2025-07-02 05:51:52.544 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:52.556 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:52.564
2025-07-02 05:51:52.577 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:52.589 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:52.599 alo = 405, ahi = 1101
2025-07-02 05:51:52.611 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:52.621 blo = 405, bhi = 1101
2025-07-02 05:51:52.629
2025-07-02 05:51:52.640 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:52.649 r"""
2025-07-02 05:51:52.656 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:52.663 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:52.669 synch point, and intraline difference marking is done on the
2025-07-02 05:51:52.674 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:52.679
2025-07-02 05:51:52.683 Example:
2025-07-02 05:51:52.688
2025-07-02 05:51:52.693 >>> d = Differ()
2025-07-02 05:51:52.697 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:52.702 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:52.707 >>> print(''.join(results), end="")
2025-07-02 05:51:52.711 - abcDefghiJkl
2025-07-02 05:51:52.720 + abcdefGhijkl
2025-07-02 05:51:52.729 """
2025-07-02 05:51:52.739
2025-07-02 05:51:52.746 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:52.753 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:52.759 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:52.765 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:52.770 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:52.775
2025-07-02 05:51:52.780 # search for the pair that matches best without being identical
2025-07-02 05:51:52.785 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:52.790 # on junk -- unless we have to)
2025-07-02 05:51:52.794 for j in range(blo, bhi):
2025-07-02 05:51:52.799 bj = b[j]
2025-07-02 05:51:52.804 cruncher.set_seq2(bj)
2025-07-02 05:51:52.809 for i in range(alo, ahi):
2025-07-02 05:51:52.813 ai = a[i]
2025-07-02 05:51:52.818 if ai == bj:
2025-07-02 05:51:52.823 if eqi is None:
2025-07-02 05:51:52.827 eqi, eqj = i, j
2025-07-02 05:51:52.832 continue
2025-07-02 05:51:52.839 cruncher.set_seq1(ai)
2025-07-02 05:51:52.849 # computing similarity is expensive, so use the quick
2025-07-02 05:51:52.859 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:52.867 # compares by a factor of 3.
2025-07-02 05:51:52.874 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:52.883 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:52.897 # of the computation is cached by cruncher
2025-07-02 05:51:52.906 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:52.917 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:52.929 cruncher.ratio() > best_ratio:
2025-07-02 05:51:52.940 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:52.948 if best_ratio < cutoff:
2025-07-02 05:51:52.956 # no non-identical "pretty close" pair
2025-07-02 05:51:52.962 if eqi is None:
2025-07-02 05:51:52.969 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:52.978 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:52.991 return
2025-07-02 05:51:53.002 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:53.011 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:53.020 else:
2025-07-02 05:51:53.027 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:53.033 eqi = None
2025-07-02 05:51:53.039
2025-07-02 05:51:53.044 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:53.049 # identical
2025-07-02 05:51:53.054
2025-07-02 05:51:53.059 # pump out diffs from before the synch point
2025-07-02 05:51:53.065 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:53.072
2025-07-02 05:51:53.079 # do intraline marking on the synch pair
2025-07-02 05:51:53.086 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:53.097 if eqi is None:
2025-07-02 05:51:53.108 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:53.118 atags = btags = ""
2025-07-02 05:51:53.127 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:53.135 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:53.142 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:53.151 if tag == 'replace':
2025-07-02 05:51:53.164 atags += '^' * la
2025-07-02 05:51:53.173 btags += '^' * lb
2025-07-02 05:51:53.181 elif tag == 'delete':
2025-07-02 05:51:53.193 atags += '-' * la
2025-07-02 05:51:53.204 elif tag == 'insert':
2025-07-02 05:51:53.212 btags += '+' * lb
2025-07-02 05:51:53.219 elif tag == 'equal':
2025-07-02 05:51:53.226 atags += ' ' * la
2025-07-02 05:51:53.233 btags += ' ' * lb
2025-07-02 05:51:53.239 else:
2025-07-02 05:51:53.246 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:53.252 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:53.259 else:
2025-07-02 05:51:53.264 # the synch pair is identical
2025-07-02 05:51:53.270 yield '  ' + aelt
2025-07-02 05:51:53.282
2025-07-02 05:51:53.292 # pump out diffs from after the synch point
2025-07-02 05:51:53.300 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:53.305
2025-07-02 05:51:53.311 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:53.318 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:53.324
2025-07-02 05:51:53.331 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:53.338 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:53.344 alo = 406, ahi = 1101
2025-07-02 05:51:53.352 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:53.359 blo = 406, bhi = 1101
2025-07-02 05:51:53.367
2025-07-02 05:51:53.380 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:53.388 g = []
2025-07-02 05:51:53.395 if alo < ahi:
2025-07-02 05:51:53.401 if blo < bhi:
2025-07-02 05:51:53.407 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:53.413 else:
2025-07-02 05:51:53.419 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:53.425 elif blo < bhi:
2025-07-02 05:51:53.432 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:53.439
2025-07-02 05:51:53.449 >       yield from g
2025-07-02 05:51:53.458
2025-07-02 05:51:53.466 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:53.473 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:53.478
2025-07-02 05:51:53.483 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:53.489 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:53.494 alo = 406, ahi = 1101
2025-07-02 05:51:53.501 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:53.507 blo = 406, bhi = 1101
2025-07-02 05:51:53.512
2025-07-02 05:51:53.517 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:53.522 r"""
2025-07-02 05:51:53.529 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:53.538 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:53.550 synch point, and intraline difference marking is done on the
2025-07-02 05:51:53.560 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:53.567
2025-07-02 05:51:53.574 Example:
2025-07-02 05:51:53.581
2025-07-02 05:51:53.587 >>> d = Differ()
2025-07-02 05:51:53.594 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:53.602 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:53.610 >>> print(''.join(results), end="")
2025-07-02 05:51:53.616 - abcDefghiJkl
2025-07-02 05:51:53.628 + abcdefGhijkl
2025-07-02 05:51:53.641 """
2025-07-02 05:51:53.647
2025-07-02 05:51:53.654 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:53.661 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:53.668 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:53.675 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:53.683 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:53.695
2025-07-02 05:51:53.704 # search for the pair that matches best without being identical
2025-07-02 05:51:53.712 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:53.719 # on junk -- unless we have to)
2025-07-02 05:51:53.727 for j in range(blo, bhi):
2025-07-02 05:51:53.739 bj = b[j]
2025-07-02 05:51:53.748 cruncher.set_seq2(bj)
2025-07-02 05:51:53.755 for i in range(alo, ahi):
2025-07-02 05:51:53.760 ai = a[i]
2025-07-02 05:51:53.765 if ai == bj:
2025-07-02 05:51:53.769 if eqi is None:
2025-07-02 05:51:53.774 eqi, eqj = i, j
2025-07-02 05:51:53.784 continue
2025-07-02 05:51:53.797 cruncher.set_seq1(ai)
2025-07-02 05:51:53.806 # computing similarity is expensive, so use the quick
2025-07-02 05:51:53.818 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:53.831 # compares by a factor of 3.
2025-07-02 05:51:53.844 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:53.855 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:53.862 # of the computation is cached by cruncher
2025-07-02 05:51:53.869 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:53.875 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:53.880 cruncher.ratio() > best_ratio:
2025-07-02 05:51:53.885 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:53.891 if best_ratio < cutoff:
2025-07-02 05:51:53.898 # no non-identical "pretty close" pair
2025-07-02 05:51:53.908 if eqi is None:
2025-07-02 05:51:53.916 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:53.924 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:53.931 return
2025-07-02 05:51:53.939 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:53.944 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:53.950 else:
2025-07-02 05:51:53.955 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:53.961 eqi = None
2025-07-02 05:51:53.966
2025-07-02 05:51:53.976 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:53.987 # identical
2025-07-02 05:51:54.001
2025-07-02 05:51:54.011 # pump out diffs from before the synch point
2025-07-02 05:51:54.019 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:54.026
2025-07-02 05:51:54.031 # do intraline marking on the synch pair
2025-07-02 05:51:54.036 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:54.041 if eqi is None:
2025-07-02 05:51:54.046 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:54.051 atags = btags = ""
2025-07-02 05:51:54.056 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:54.063 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:54.073 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:54.081 if tag == 'replace':
2025-07-02 05:51:54.089 atags += '^' * la
2025-07-02 05:51:54.100 btags += '^' * lb
2025-07-02 05:51:54.108 elif tag == 'delete':
2025-07-02 05:51:54.114 atags += '-' * la
2025-07-02 05:51:54.119 elif tag == 'insert':
2025-07-02 05:51:54.124 btags += '+' * lb
2025-07-02 05:51:54.128 elif tag == 'equal':
2025-07-02 05:51:54.133 atags += ' ' * la
2025-07-02 05:51:54.138 btags += ' ' * lb
2025-07-02 05:51:54.143 else:
2025-07-02 05:51:54.149 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:54.154 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:54.159 else:
2025-07-02 05:51:54.167 # the synch pair is identical
2025-07-02 05:51:54.176 yield '  ' + aelt
2025-07-02 05:51:54.184
2025-07-02 05:51:54.191 # pump out diffs from after the synch point
2025-07-02 05:51:54.197 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:54.203
2025-07-02 05:51:54.209 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:54.216 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:54.224
2025-07-02 05:51:54.238 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:54.251 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:54.261 alo = 407, ahi = 1101
2025-07-02 05:51:54.275 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:54.284 blo = 407, bhi = 1101
2025-07-02 05:51:54.292
2025-07-02 05:51:54.300 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:54.305 g = []
2025-07-02 05:51:54.317 if alo < ahi:
2025-07-02 05:51:54.326 if blo < bhi:
2025-07-02 05:51:54.337 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:54.348 else:
2025-07-02 05:51:54.359 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:54.371 elif blo < bhi:
2025-07-02 05:51:54.384 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:54.393
2025-07-02 05:51:54.400 >       yield from g
2025-07-02 05:51:54.407
2025-07-02 05:51:54.414 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:54.421 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:54.427
2025-07-02 05:51:54.440 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:54.452 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:54.460 alo = 407, ahi = 1101
2025-07-02 05:51:54.468 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:54.476 blo = 407, bhi = 1101
2025-07-02 05:51:54.482
2025-07-02 05:51:54.489 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:54.496 r"""
2025-07-02 05:51:54.503 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:54.511 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:54.519 synch point, and intraline difference marking is done on the
2025-07-02 05:51:54.531 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:54.539
2025-07-02 05:51:54.547 Example:
2025-07-02 05:51:54.553
2025-07-02 05:51:54.558 >>> d = Differ()
2025-07-02 05:51:54.563 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:54.568 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:54.573 >>> print(''.join(results), end="")
2025-07-02 05:51:54.578 - abcDefghiJkl
2025-07-02 05:51:54.589 + abcdefGhijkl
2025-07-02 05:51:54.601 """
2025-07-02 05:51:54.606
2025-07-02 05:51:54.611 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:54.616 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:54.621 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:54.627 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:54.634 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:54.646
2025-07-02 05:51:54.655 # search for the pair that matches best without being identical
2025-07-02 05:51:54.663 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:54.670 # on junk -- unless we have to)
2025-07-02 05:51:54.676 for j in range(blo, bhi):
2025-07-02 05:51:54.685 bj = b[j]
2025-07-02 05:51:54.702 cruncher.set_seq2(bj)
2025-07-02 05:51:54.712 for i in range(alo, ahi):
2025-07-02 05:51:54.721 ai = a[i]
2025-07-02 05:51:54.731 if ai == bj:
2025-07-02 05:51:54.743 if eqi is None:
2025-07-02 05:51:54.753 eqi, eqj = i, j
2025-07-02 05:51:54.761 continue
2025-07-02 05:51:54.768 cruncher.set_seq1(ai)
2025-07-02 05:51:54.775 # computing similarity is expensive, so use the quick
2025-07-02 05:51:54.782 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:54.793 # compares by a factor of 3.
2025-07-02 05:51:54.804 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:54.812 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:54.820 # of the computation is cached by cruncher
2025-07-02 05:51:54.826 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:54.836 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:54.846 cruncher.ratio() > best_ratio:
2025-07-02 05:51:54.854 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:54.861 if best_ratio < cutoff:
2025-07-02 05:51:54.868 # no non-identical "pretty close" pair
2025-07-02 05:51:54.875 if eqi is None:
2025-07-02 05:51:54.882 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:54.889 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:54.896 return
2025-07-02 05:51:54.904 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:54.912 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:54.923 else:
2025-07-02 05:51:54.931 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:54.944 eqi = None
2025-07-02 05:51:54.952
2025-07-02 05:51:54.960 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:54.968 # identical
2025-07-02 05:51:54.980
2025-07-02 05:51:54.989 # pump out diffs from before the synch point
2025-07-02 05:51:55.001 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:55.010
2025-07-02 05:51:55.018 # do intraline marking on the synch pair
2025-07-02 05:51:55.027 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:55.040 if eqi is None:
2025-07-02 05:51:55.050 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:55.059 atags = btags = ""
2025-07-02 05:51:55.068 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:55.076 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:55.084 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:55.090 if tag == 'replace':
2025-07-02 05:51:55.102 atags += '^' * la
2025-07-02 05:51:55.111 btags += '^' * lb
2025-07-02 05:51:55.118 elif tag == 'delete':
2025-07-02 05:51:55.124 atags += '-' * la
2025-07-02 05:51:55.130 elif tag == 'insert':
2025-07-02 05:51:55.135 btags += '+' * lb
2025-07-02 05:51:55.141 elif tag == 'equal':
2025-07-02 05:51:55.146 atags += ' ' * la
2025-07-02 05:51:55.157 btags += ' ' * lb
2025-07-02 05:51:55.168 else:
2025-07-02 05:51:55.181 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:55.191 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:55.204 else:
2025-07-02 05:51:55.213 # the synch pair is identical
2025-07-02 05:51:55.219 yield '  ' + aelt
2025-07-02 05:51:55.225
2025-07-02 05:51:55.229 # pump out diffs from after the synch point
2025-07-02 05:51:55.234 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:55.239
2025-07-02 05:51:55.245 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:55.250 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:55.255
2025-07-02 05:51:55.260 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:55.266 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:55.273 alo = 408, ahi = 1101
2025-07-02 05:51:55.282 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:55.289 blo = 408, bhi = 1101
2025-07-02 05:51:55.295
2025-07-02 05:51:55.300 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:55.304 g = []
2025-07-02 05:51:55.309 if alo < ahi:
2025-07-02 05:51:55.313 if blo < bhi:
2025-07-02 05:51:55.318 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:55.322 else:
2025-07-02 05:51:55.327 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:55.334 elif blo < bhi:
2025-07-02 05:51:55.344 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:55.354
2025-07-02 05:51:55.361 >       yield from g
2025-07-02 05:51:55.368
2025-07-02 05:51:55.376 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:55.385 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:55.391
2025-07-02 05:51:55.398 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:55.406 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:55.414 alo = 408, ahi = 1101
2025-07-02 05:51:55.422 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:55.428 blo = 408, bhi = 1101
2025-07-02 05:51:55.434
2025-07-02 05:51:55.448 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:55.455 r"""
2025-07-02 05:51:55.463 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:55.470 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:55.475 synch point, and intraline difference marking is done on the
2025-07-02 05:51:55.489 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:55.499
2025-07-02 05:51:55.510 Example:
2025-07-02 05:51:55.519
2025-07-02 05:51:55.525 >>> d = Differ()
2025-07-02 05:51:55.537 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:55.549 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:55.559 >>> print(''.join(results), end="")
2025-07-02 05:51:55.571 - abcDefghiJkl
2025-07-02 05:51:55.588 + abcdefGhijkl
2025-07-02 05:51:55.604 """
2025-07-02 05:51:55.615
2025-07-02 05:51:55.624 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:55.632 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:55.639 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:55.646 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:55.652 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:55.659
2025-07-02 05:51:55.665 # search for the pair that matches best without being identical
2025-07-02 05:51:55.678 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:55.687 # on junk -- unless we have to)
2025-07-02 05:51:55.698 for j in range(blo, bhi):
2025-07-02 05:51:55.710 bj = b[j]
2025-07-02 05:51:55.720 cruncher.set_seq2(bj)
2025-07-02 05:51:55.729 for i in range(alo, ahi):
2025-07-02 05:51:55.738 ai = a[i]
2025-07-02 05:51:55.744 if ai == bj:
2025-07-02 05:51:55.750 if eqi is None:
2025-07-02 05:51:55.763 eqi, eqj = i, j
2025-07-02 05:51:55.773 continue
2025-07-02 05:51:55.780 cruncher.set_seq1(ai)
2025-07-02 05:51:55.787 # computing similarity is expensive, so use the quick
2025-07-02 05:51:55.793 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:55.799 # compares by a factor of 3.
2025-07-02 05:51:55.805 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:55.811 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:55.824 # of the computation is cached by cruncher
2025-07-02 05:51:55.833 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:55.847 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:55.863 cruncher.ratio() > best_ratio:
2025-07-02 05:51:55.874 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:55.883 if best_ratio < cutoff:
2025-07-02 05:51:55.891 # no non-identical "pretty close" pair
2025-07-02 05:51:55.900 if eqi is None:
2025-07-02 05:51:55.906 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:55.912 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:55.918 return
2025-07-02 05:51:55.928 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:55.938 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:55.947 else:
2025-07-02 05:51:55.956 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:55.966 eqi = None
2025-07-02 05:51:55.977
2025-07-02 05:51:55.983 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:55.988 # identical
2025-07-02 05:51:55.994
2025-07-02 05:51:56.000 # pump out diffs from before the synch point
2025-07-02 05:51:56.005 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:56.009
2025-07-02 05:51:56.014 # do intraline marking on the synch pair
2025-07-02 05:51:56.028 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:56.040 if eqi is None:
2025-07-02 05:51:56.051 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:56.064 atags = btags = ""
2025-07-02 05:51:56.073 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:56.083 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:56.093 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:56.101 if tag == 'replace':
2025-07-02 05:51:56.108 atags += '^' * la
2025-07-02 05:51:56.115 btags += '^' * lb
2025-07-02 05:51:56.123 elif tag == 'delete':
2025-07-02 05:51:56.132 atags += '-' * la
2025-07-02 05:51:56.138 elif tag == 'insert':
2025-07-02 05:51:56.143 btags += '+' * lb
2025-07-02 05:51:56.149 elif tag == 'equal':
2025-07-02 05:51:56.157 atags += ' ' * la
2025-07-02 05:51:56.164 btags += ' ' * lb
2025-07-02 05:51:56.173 else:
2025-07-02 05:51:56.180 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:56.189 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:56.198 else:
2025-07-02 05:51:56.207 # the synch pair is identical
2025-07-02 05:51:56.219 yield '  ' + aelt
2025-07-02 05:51:56.228
2025-07-02 05:51:56.237 # pump out diffs from after the synch point
2025-07-02 05:51:56.244 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:56.251
2025-07-02 05:51:56.257 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:56.264 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:56.270
2025-07-02 05:51:56.276 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:56.283 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:56.289 alo = 409, ahi = 1101
2025-07-02 05:51:56.297 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:56.304 blo = 409, bhi = 1101
2025-07-02 05:51:56.310
2025-07-02 05:51:56.316 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:56.322 g = []
2025-07-02 05:51:56.328 if alo < ahi:
2025-07-02 05:51:56.335 if blo < bhi:
2025-07-02 05:51:56.340 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:56.345 else:
2025-07-02 05:51:56.350 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:56.359 elif blo < bhi:
2025-07-02 05:51:56.368 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:56.377
2025-07-02 05:51:56.386 >       yield from g
2025-07-02 05:51:56.392
2025-07-02 05:51:56.399 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:56.407 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:56.414
2025-07-02 05:51:56.423 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:56.431 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:56.437 alo = 409, ahi = 1101
2025-07-02 05:51:56.443 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:56.448 blo = 409, bhi = 1101
2025-07-02 05:51:56.455
2025-07-02 05:51:56.462 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:56.469 r"""
2025-07-02 05:51:56.476 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:56.484 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:56.493 synch point, and intraline difference marking is done on the
2025-07-02 05:51:56.501 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:56.511
2025-07-02 05:51:56.526 Example:
2025-07-02 05:51:56.536
2025-07-02 05:51:56.544 >>> d = Differ()
2025-07-02 05:51:56.551 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:56.557 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:56.562 >>> print(''.join(results), end="")
2025-07-02 05:51:56.567 - abcDefghiJkl
2025-07-02 05:51:56.577 + abcdefGhijkl
2025-07-02 05:51:56.587 """
2025-07-02 05:51:56.592
2025-07-02 05:51:56.598 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:56.603 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:56.608 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:56.613 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:56.619 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:56.625
2025-07-02 05:51:56.631 # search for the pair that matches best without being identical
2025-07-02 05:51:56.638 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:56.644 # on junk -- unless we have to)
2025-07-02 05:51:56.650 for j in range(blo, bhi):
2025-07-02 05:51:56.656 bj = b[j]
2025-07-02 05:51:56.663 cruncher.set_seq2(bj)
2025-07-02 05:51:56.669 for i in range(alo, ahi):
2025-07-02 05:51:56.675 ai = a[i]
2025-07-02 05:51:56.688 if ai == bj:
2025-07-02 05:51:56.698 if eqi is None:
2025-07-02 05:51:56.708 eqi, eqj = i, j
2025-07-02 05:51:56.714 continue
2025-07-02 05:51:56.721 cruncher.set_seq1(ai)
2025-07-02 05:51:56.728 # computing similarity is expensive, so use the quick
2025-07-02 05:51:56.736 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:56.744 # compares by a factor of 3.
2025-07-02 05:51:56.752 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:56.760 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:56.768 # of the computation is cached by cruncher
2025-07-02 05:51:56.775 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:56.787 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:56.796 cruncher.ratio() > best_ratio:
2025-07-02 05:51:56.803 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:56.810 if best_ratio < cutoff:
2025-07-02 05:51:56.818 # no non-identical "pretty close" pair
2025-07-02 05:51:56.826 if eqi is None:
2025-07-02 05:51:56.833 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:56.841 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:56.849 return
2025-07-02 05:51:56.856 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:56.863 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:56.870 else:
2025-07-02 05:51:56.881 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:56.894 eqi = None
2025-07-02 05:51:56.904
2025-07-02 05:51:56.911 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:56.925 # identical
2025-07-02 05:51:56.934
2025-07-02 05:51:56.944 # pump out diffs from before the synch point
2025-07-02 05:51:56.951 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:56.957
2025-07-02 05:51:56.963 # do intraline marking on the synch pair
2025-07-02 05:51:56.968 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:56.973 if eqi is None:
2025-07-02 05:51:56.980 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:56.987 atags = btags = ""
2025-07-02 05:51:56.994 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:57.000 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:57.007 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:57.016 if tag == 'replace':
2025-07-02 05:51:57.023 atags += '^' * la
2025-07-02 05:51:57.029 btags += '^' * lb
2025-07-02 05:51:57.036 elif tag == 'delete':
2025-07-02 05:51:57.043 atags += '-' * la
2025-07-02 05:51:57.049 elif tag == 'insert':
2025-07-02 05:51:57.054 btags += '+' * lb
2025-07-02 05:51:57.059 elif tag == 'equal':
2025-07-02 05:51:57.066 atags += ' ' * la
2025-07-02 05:51:57.074 btags += ' ' * lb
2025-07-02 05:51:57.080 else:
2025-07-02 05:51:57.086 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:57.094 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:57.104 else:
2025-07-02 05:51:57.112 # the synch pair is identical
2025-07-02 05:51:57.119 yield '  ' + aelt
2025-07-02 05:51:57.124
2025-07-02 05:51:57.129 # pump out diffs from after the synch point
2025-07-02 05:51:57.135 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:57.140
2025-07-02 05:51:57.146 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:57.150 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:57.156
2025-07-02 05:51:57.166 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:57.175 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:57.181 alo = 410, ahi = 1101
2025-07-02 05:51:57.191 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:57.205 blo = 410, bhi = 1101
2025-07-02 05:51:57.214
2025-07-02 05:51:57.220 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:57.227 g = []
2025-07-02 05:51:57.240 if alo < ahi:
2025-07-02 05:51:57.248 if blo < bhi:
2025-07-02 05:51:57.254 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:57.261 else:
2025-07-02 05:51:57.269 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:57.275 elif blo < bhi:
2025-07-02 05:51:57.282 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:57.294
2025-07-02 05:51:57.304 >       yield from g
2025-07-02 05:51:57.313
2025-07-02 05:51:57.326 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:57.335 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:57.346
2025-07-02 05:51:57.355 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:57.362 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:57.374 alo = 410, ahi = 1101
2025-07-02 05:51:57.383 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:57.389 blo = 410, bhi = 1101
2025-07-02 05:51:57.394
2025-07-02 05:51:57.399 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:57.404 r"""
2025-07-02 05:51:57.413 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:57.421 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:57.428 synch point, and intraline difference marking is done on the
2025-07-02 05:51:57.435 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:57.443
2025-07-02 05:51:57.451 Example:
2025-07-02 05:51:57.459
2025-07-02 05:51:57.466 >>> d = Differ()
2025-07-02 05:51:57.479 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:57.488 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:57.493 >>> print(''.join(results), end="")
2025-07-02 05:51:57.499 - abcDefghiJkl
2025-07-02 05:51:57.509 + abcdefGhijkl
2025-07-02 05:51:57.518 """
2025-07-02 05:51:57.531
2025-07-02 05:51:57.541 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:57.554 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:57.565 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:57.574 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:57.585 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:57.597
2025-07-02 05:51:57.610 # search for the pair that matches best without being identical
2025-07-02 05:51:57.623 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:57.633 # on junk -- unless we have to)
2025-07-02 05:51:57.645 for j in range(blo, bhi):
2025-07-02 05:51:57.657 bj = b[j]
2025-07-02 05:51:57.670 cruncher.set_seq2(bj)
2025-07-02 05:51:57.682 for i in range(alo, ahi):
2025-07-02 05:51:57.697 ai = a[i]
2025-07-02 05:51:57.710 if ai == bj:
2025-07-02 05:51:57.723 if eqi is None:
2025-07-02 05:51:57.733 eqi, eqj = i, j
2025-07-02 05:51:57.742 continue
2025-07-02 05:51:57.750 cruncher.set_seq1(ai)
2025-07-02 05:51:57.756 # computing similarity is expensive, so use the quick
2025-07-02 05:51:57.763 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:57.769 # compares by a factor of 3.
2025-07-02 05:51:57.776 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:57.783 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:57.789 # of the computation is cached by cruncher
2025-07-02 05:51:57.795 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:57.801 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:57.813 cruncher.ratio() > best_ratio:
2025-07-02 05:51:57.826 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:57.838 if best_ratio < cutoff:
2025-07-02 05:51:57.846 # no non-identical "pretty close" pair
2025-07-02 05:51:57.858 if eqi is None:
2025-07-02 05:51:57.870 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:57.879 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:57.887 return
2025-07-02 05:51:57.897 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:57.907 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:57.914 else:
2025-07-02 05:51:57.920 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:57.926 eqi = None
2025-07-02 05:51:57.932
2025-07-02 05:51:57.939 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:57.947 # identical
2025-07-02 05:51:57.959
2025-07-02 05:51:57.968 # pump out diffs from before the synch point
2025-07-02 05:51:57.976 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:57.982
2025-07-02 05:51:57.989 # do intraline marking on the synch pair
2025-07-02 05:51:57.993 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:57.999 if eqi is None:
2025-07-02 05:51:58.005 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:58.014 atags = btags = ""
2025-07-02 05:51:58.022 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:58.030 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:58.038 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:58.046 if tag == 'replace':
2025-07-02 05:51:58.055 atags += '^' * la
2025-07-02 05:51:58.066 btags += '^' * lb
2025-07-02 05:51:58.075 elif tag == 'delete':
2025-07-02 05:51:58.082 atags += '-' * la
2025-07-02 05:51:58.088 elif tag == 'insert':
2025-07-02 05:51:58.094 btags += '+' * lb
2025-07-02 05:51:58.104 elif tag == 'equal':
2025-07-02 05:51:58.116 atags += ' ' * la
2025-07-02 05:51:58.124 btags += ' ' * lb
2025-07-02 05:51:58.130 else:
2025-07-02 05:51:58.137 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:58.143 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:58.150 else:
2025-07-02 05:51:58.156 # the synch pair is identical
2025-07-02 05:51:58.162 yield '  ' + aelt
2025-07-02 05:51:58.168
2025-07-02 05:51:58.175 # pump out diffs from after the synch point
2025-07-02 05:51:58.181 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:58.188
2025-07-02 05:51:58.194 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:58.201 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:58.207
2025-07-02 05:51:58.214 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:58.221 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:58.228 alo = 411, ahi = 1101
2025-07-02 05:51:58.235 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:58.242 blo = 411, bhi = 1101
2025-07-02 05:51:58.248
2025-07-02 05:51:58.254 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:58.261 g = []
2025-07-02 05:51:58.267 if alo < ahi:
2025-07-02 05:51:58.273 if blo < bhi:
2025-07-02 05:51:58.280 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:58.286 else:
2025-07-02 05:51:58.293 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:58.299 elif blo < bhi:
2025-07-02 05:51:58.305 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:58.312
2025-07-02 05:51:58.318 >       yield from g
2025-07-02 05:51:58.324
2025-07-02 05:51:58.331 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:58.338 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:58.344
2025-07-02 05:51:58.352 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:58.365 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:58.374 alo = 411, ahi = 1101
2025-07-02 05:51:58.385 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:58.394 blo = 411, bhi = 1101
2025-07-02 05:51:58.400
2025-07-02 05:51:58.407 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:58.414 r"""
2025-07-02 05:51:58.423 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:58.431 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:58.439 synch point, and intraline difference marking is done on the
2025-07-02 05:51:58.445 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:58.452
2025-07-02 05:51:58.458 Example:
2025-07-02 05:51:58.465
2025-07-02 05:51:58.473 >>> d = Differ()
2025-07-02 05:51:58.480 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:58.488 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:58.496 >>> print(''.join(results), end="")
2025-07-02 05:51:58.504 - abcDefghiJkl
2025-07-02 05:51:58.518 + abcdefGhijkl
2025-07-02 05:51:58.536 """
2025-07-02 05:51:58.547
2025-07-02 05:51:58.558 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:58.569 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:58.576 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:58.583 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:58.590 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:58.596
2025-07-02 05:51:58.604 # search for the pair that matches best without being identical
2025-07-02 05:51:58.615 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:58.623 # on junk -- unless we have to)
2025-07-02 05:51:58.630 for j in range(blo, bhi):
2025-07-02 05:51:58.639 bj = b[j]
2025-07-02 05:51:58.648 cruncher.set_seq2(bj)
2025-07-02 05:51:58.655 for i in range(alo, ahi):
2025-07-02 05:51:58.662 ai = a[i]
2025-07-02 05:51:58.673 if ai == bj:
2025-07-02 05:51:58.685 if eqi is None:
2025-07-02 05:51:58.695 eqi, eqj = i, j
2025-07-02 05:51:58.704 continue
2025-07-02 05:51:58.718 cruncher.set_seq1(ai)
2025-07-02 05:51:58.729 # computing similarity is expensive, so use the quick
2025-07-02 05:51:58.742 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:58.753 # compares by a factor of 3.
2025-07-02 05:51:58.763 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:58.771 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:58.776 # of the computation is cached by cruncher
2025-07-02 05:51:58.783 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:58.792 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:58.804 cruncher.ratio() > best_ratio:
2025-07-02 05:51:58.814 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:58.822 if best_ratio < cutoff:
2025-07-02 05:51:58.831 # no non-identical "pretty close" pair
2025-07-02 05:51:58.843 if eqi is None:
2025-07-02 05:51:58.852 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:58.860 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:58.871 return
2025-07-02 05:51:58.884 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:58.895 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:58.904 else:
2025-07-02 05:51:58.913 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:51:58.920 eqi = None
2025-07-02 05:51:58.926
2025-07-02 05:51:58.932 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:51:58.947 # identical
2025-07-02 05:51:58.957
2025-07-02 05:51:58.966 # pump out diffs from before the synch point
2025-07-02 05:51:58.975 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:51:58.986
2025-07-02 05:51:58.999 # do intraline marking on the synch pair
2025-07-02 05:51:59.010 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:51:59.022 if eqi is None:
2025-07-02 05:51:59.036 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:51:59.048 atags = btags = ""
2025-07-02 05:51:59.056 cruncher.set_seqs(aelt, belt)
2025-07-02 05:51:59.063 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:51:59.070 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:51:59.077 if tag == 'replace':
2025-07-02 05:51:59.083 atags += '^' * la
2025-07-02 05:51:59.089 btags += '^' * lb
2025-07-02 05:51:59.095 elif tag == 'delete':
2025-07-02 05:51:59.102 atags += '-' * la
2025-07-02 05:51:59.109 elif tag == 'insert':
2025-07-02 05:51:59.120 btags += '+' * lb
2025-07-02 05:51:59.130 elif tag == 'equal':
2025-07-02 05:51:59.144 atags += ' ' * la
2025-07-02 05:51:59.151 btags += ' ' * lb
2025-07-02 05:51:59.159 else:
2025-07-02 05:51:59.169 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:51:59.176 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:51:59.182 else:
2025-07-02 05:51:59.188 # the synch pair is identical
2025-07-02 05:51:59.194 yield '  ' + aelt
2025-07-02 05:51:59.205
2025-07-02 05:51:59.214 # pump out diffs from after the synch point
2025-07-02 05:51:59.223 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:51:59.231
2025-07-02 05:51:59.241 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:51:59.251 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:59.257
2025-07-02 05:51:59.269 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:59.282 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:59.293 alo = 412, ahi = 1101
2025-07-02 05:51:59.303 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:59.311 blo = 412, bhi = 1101
2025-07-02 05:51:59.319
2025-07-02 05:51:59.328 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:59.339 g = []
2025-07-02 05:51:59.349 if alo < ahi:
2025-07-02 05:51:59.363 if blo < bhi:
2025-07-02 05:51:59.373 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:59.380 else:
2025-07-02 05:51:59.387 g = self._dump('-', a, alo, ahi)
2025-07-02 05:51:59.394 elif blo < bhi:
2025-07-02 05:51:59.406 g = self._dump('+', b, blo, bhi)
2025-07-02 05:51:59.419
2025-07-02 05:51:59.430 >       yield from g
2025-07-02 05:51:59.444
2025-07-02 05:51:59.455 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:51:59.465 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:51:59.471
2025-07-02 05:51:59.478 self = <difflib.Differ object at [hex]>
2025-07-02 05:51:59.486 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:51:59.499 alo = 412, ahi = 1101
2025-07-02 05:51:59.509 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:51:59.516 blo = 412, bhi = 1101
2025-07-02 05:51:59.523
2025-07-02 05:51:59.530 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:51:59.537 r"""
2025-07-02 05:51:59.544 When replacing one block of lines with another, search the blocks
2025-07-02 05:51:59.551 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:51:59.558 synch point, and intraline difference marking is done on the
2025-07-02 05:51:59.571 similar pair. Lots of work, but often worth it.
2025-07-02 05:51:59.581
2025-07-02 05:51:59.589 Example:
2025-07-02 05:51:59.596
2025-07-02 05:51:59.602 >>> d = Differ()
2025-07-02 05:51:59.609 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:51:59.615 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:51:59.620 >>> print(''.join(results), end="")
2025-07-02 05:51:59.626 - abcDefghiJkl
2025-07-02 05:51:59.647 + abcdefGhijkl
2025-07-02 05:51:59.667 """
2025-07-02 05:51:59.673
2025-07-02 05:51:59.679 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:51:59.684 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:51:59.689 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:51:59.694 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:51:59.699 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:51:59.707
2025-07-02 05:51:59.719 # search for the pair that matches best without being identical
2025-07-02 05:51:59.728 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:51:59.735 # on junk -- unless we have to)
2025-07-02 05:51:59.742 for j in range(blo, bhi):
2025-07-02 05:51:59.748 bj = b[j]
2025-07-02 05:51:59.753 cruncher.set_seq2(bj)
2025-07-02 05:51:59.759 for i in range(alo, ahi):
2025-07-02 05:51:59.764 ai = a[i]
2025-07-02 05:51:59.769 if ai == bj:
2025-07-02 05:51:59.785 if eqi is None:
2025-07-02 05:51:59.796 eqi, eqj = i, j
2025-07-02 05:51:59.803 continue
2025-07-02 05:51:59.811 cruncher.set_seq1(ai)
2025-07-02 05:51:59.817 # computing similarity is expensive, so use the quick
2025-07-02 05:51:59.822 # upper bounds first -- have seen this speed up messy
2025-07-02 05:51:59.829 # compares by a factor of 3.
2025-07-02 05:51:59.835 # note that ratio() is only expensive to compute the first
2025-07-02 05:51:59.841 # time it's called on a sequence pair; the expensive part
2025-07-02 05:51:59.847 # of the computation is cached by cruncher
2025-07-02 05:51:59.853 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:51:59.866 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:51:59.878 cruncher.ratio() > best_ratio:
2025-07-02 05:51:59.894 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:51:59.908 if best_ratio < cutoff:
2025-07-02 05:51:59.919 # no non-identical "pretty close" pair
2025-07-02 05:51:59.931 if eqi is None:
2025-07-02 05:51:59.944 # no identical pair either -- treat it as a straight replace
2025-07-02 05:51:59.954 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:51:59.961 return
2025-07-02 05:51:59.968 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:51:59.975 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:51:59.982 else:
2025-07-02 05:51:59.994 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:00.004 eqi = None
2025-07-02 05:52:00.017
2025-07-02 05:52:00.034 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:00.045 # identical
2025-07-02 05:52:00.052
2025-07-02 05:52:00.059 # pump out diffs from before the synch point
2025-07-02 05:52:00.066 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:00.075
2025-07-02 05:52:00.083 # do intraline marking on the synch pair
2025-07-02 05:52:00.095 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:00.106 if eqi is None:
2025-07-02 05:52:00.115 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:00.124 atags = btags = ""
2025-07-02 05:52:00.131 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:00.144 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:00.154 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:00.165 if tag == 'replace':
2025-07-02 05:52:00.176 atags += '^' * la
2025-07-02 05:52:00.189 btags += '^' * lb
2025-07-02 05:52:00.199 elif tag == 'delete':
2025-07-02 05:52:00.213 atags += '-' * la
2025-07-02 05:52:00.225 elif tag == 'insert':
2025-07-02 05:52:00.236 btags += '+' * lb
2025-07-02 05:52:00.249 elif tag == 'equal':
2025-07-02 05:52:00.260 atags += ' ' * la
2025-07-02 05:52:00.269 btags += ' ' * lb
2025-07-02 05:52:00.281 else:
2025-07-02 05:52:00.293 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:00.305 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:00.315 else:
2025-07-02 05:52:00.323 # the synch pair is identical
2025-07-02 05:52:00.331 yield '  ' + aelt
2025-07-02 05:52:00.339
2025-07-02 05:52:00.350 # pump out diffs from after the synch point
2025-07-02 05:52:00.360 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:00.366
2025-07-02 05:52:00.374 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:00.381 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:00.386
2025-07-02 05:52:00.393 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:00.401 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:00.407 alo = 413, ahi = 1101
2025-07-02 05:52:00.412 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:00.418 blo = 413, bhi = 1101
2025-07-02 05:52:00.428
2025-07-02 05:52:00.437 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:00.445 g = []
2025-07-02 05:52:00.452 if alo < ahi:
2025-07-02 05:52:00.459 if blo < bhi:
2025-07-02 05:52:00.465 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:00.472 else:
2025-07-02 05:52:00.479 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:00.484 elif blo < bhi:
2025-07-02 05:52:00.496 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:00.507
2025-07-02 05:52:00.518 >       yield from g
2025-07-02 05:52:00.531
2025-07-02 05:52:00.540 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:00.548 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:00.555
2025-07-02 05:52:00.562 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:00.569 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:00.582 alo = 413, ahi = 1101
2025-07-02 05:52:00.595 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:00.604 blo = 413, bhi = 1101
2025-07-02 05:52:00.612
2025-07-02 05:52:00.620 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:00.626 r"""
2025-07-02 05:52:00.635 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:00.646 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:00.655 synch point, and intraline difference marking is done on the
2025-07-02 05:52:00.665 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:00.678
2025-07-02 05:52:00.690 Example:
2025-07-02 05:52:00.699
2025-07-02 05:52:00.707 >>> d = Differ()
2025-07-02 05:52:00.715 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:00.722 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:00.729 >>> print(''.join(results), end="")
2025-07-02 05:52:00.735 - abcDefghiJkl
2025-07-02 05:52:00.748 + abcdefGhijkl
2025-07-02 05:52:00.760 """
2025-07-02 05:52:00.767
2025-07-02 05:52:00.774 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:00.784 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:00.794 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:00.807 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:00.818 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:00.828
2025-07-02 05:52:00.836 # search for the pair that matches best without being identical
2025-07-02 05:52:00.844 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:00.851 # on junk -- unless we have to)
2025-07-02 05:52:00.857 for j in range(blo, bhi):
2025-07-02 05:52:00.863 bj = b[j]
2025-07-02 05:52:00.871 cruncher.set_seq2(bj)
2025-07-02 05:52:00.882 for i in range(alo, ahi):
2025-07-02 05:52:00.891 ai = a[i]
2025-07-02 05:52:00.901 if ai == bj:
2025-07-02 05:52:00.913 if eqi is None:
2025-07-02 05:52:00.923 eqi, eqj = i, j
2025-07-02 05:52:00.934 continue
2025-07-02 05:52:00.944 cruncher.set_seq1(ai)
2025-07-02 05:52:00.958 # computing similarity is expensive, so use the quick
2025-07-02 05:52:00.969 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:00.980 # compares by a factor of 3.
2025-07-02 05:52:00.992 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:01.005 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:01.019 # of the computation is cached by cruncher
2025-07-02 05:52:01.034 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:01.047 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:01.057 cruncher.ratio() > best_ratio:
2025-07-02 05:52:01.067 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:01.081 if best_ratio < cutoff:
2025-07-02 05:52:01.093 # no non-identical "pretty close" pair
2025-07-02 05:52:01.101 if eqi is None:
2025-07-02 05:52:01.107 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:01.113 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:01.119 return
2025-07-02 05:52:01.126 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:01.135 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:01.147 else:
2025-07-02 05:52:01.157 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:01.165 eqi = None
2025-07-02 05:52:01.174
2025-07-02 05:52:01.186 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:01.198 # identical
2025-07-02 05:52:01.211
2025-07-02 05:52:01.224 # pump out diffs from before the synch point
2025-07-02 05:52:01.237 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:01.246
2025-07-02 05:52:01.255 # do intraline marking on the synch pair
2025-07-02 05:52:01.264 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:01.272 if eqi is None:
2025-07-02 05:52:01.280 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:01.288 atags = btags = ""
2025-07-02 05:52:01.297 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:01.305 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:01.313 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:01.320 if tag == 'replace':
2025-07-02 05:52:01.327 atags += '^' * la
2025-07-02 05:52:01.335 btags += '^' * lb
2025-07-02 05:52:01.347 elif tag == 'delete':
2025-07-02 05:52:01.358 atags += '-' * la
2025-07-02 05:52:01.369 elif tag == 'insert':
2025-07-02 05:52:01.377 btags += '+' * lb
2025-07-02 05:52:01.383 elif tag == 'equal':
2025-07-02 05:52:01.388 atags += ' ' * la
2025-07-02 05:52:01.393 btags += ' ' * lb
2025-07-02 05:52:01.399 else:
2025-07-02 05:52:01.405 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:01.412 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:01.418 else:
2025-07-02 05:52:01.427 # the synch pair is identical
2025-07-02 05:52:01.434 yield '  ' + aelt
2025-07-02 05:52:01.441
2025-07-02 05:52:01.447 # pump out diffs from after the synch point
2025-07-02 05:52:01.455 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:01.461
2025-07-02 05:52:01.468 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:01.476 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:01.484
2025-07-02 05:52:01.492 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:01.500 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:01.507 alo = 414, ahi = 1101
2025-07-02 05:52:01.516 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:01.523 blo = 414, bhi = 1101
2025-07-02 05:52:01.530
2025-07-02 05:52:01.538 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:01.547 g = []
2025-07-02 05:52:01.558 if alo < ahi:
2025-07-02 05:52:01.568 if blo < bhi:
2025-07-02 05:52:01.579 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:01.587 else:
2025-07-02 05:52:01.594 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:01.604 elif blo < bhi:
2025-07-02 05:52:01.617 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:01.629
2025-07-02 05:52:01.638 >       yield from g
2025-07-02 05:52:01.644
2025-07-02 05:52:01.649 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:01.656 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:01.662
2025-07-02 05:52:01.669 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:01.676 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:01.683 alo = 414, ahi = 1101
2025-07-02 05:52:01.690 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:01.697 blo = 414, bhi = 1101
2025-07-02 05:52:01.704
2025-07-02 05:52:01.711 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:01.718 r"""
2025-07-02 05:52:01.725 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:01.731 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:01.738 synch point, and intraline difference marking is done on the
2025-07-02 05:52:01.744 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:01.751
2025-07-02 05:52:01.758 Example:
2025-07-02 05:52:01.765
2025-07-02 05:52:01.772 >>> d = Differ()
2025-07-02 05:52:01.779 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:01.788 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:01.795 >>> print(''.join(results), end="")
2025-07-02 05:52:01.803 - abcDefghiJkl
2025-07-02 05:52:01.824 + abcdefGhijkl
2025-07-02 05:52:01.838 """
2025-07-02 05:52:01.843
2025-07-02 05:52:01.852 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:01.862 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:01.870 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:01.877 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:01.889 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:01.897
2025-07-02 05:52:01.909 # search for the pair that matches best without being identical
2025-07-02 05:52:01.919 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:01.926 # on junk -- unless we have to)
2025-07-02 05:52:01.939 for j in range(blo, bhi):
2025-07-02 05:52:01.950 bj = b[j]
2025-07-02 05:52:01.956 cruncher.set_seq2(bj)
2025-07-02 05:52:01.963 for i in range(alo, ahi):
2025-07-02 05:52:01.970 ai = a[i]
2025-07-02 05:52:01.981 if ai == bj:
2025-07-02 05:52:01.989 if eqi is None:
2025-07-02 05:52:01.998 eqi, eqj = i, j
2025-07-02 05:52:02.005 continue
2025-07-02 05:52:02.014 cruncher.set_seq1(ai)
2025-07-02 05:52:02.023 # computing similarity is expensive, so use the quick
2025-07-02 05:52:02.031 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:02.037 # compares by a factor of 3.
2025-07-02 05:52:02.046 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:02.055 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:02.063 # of the computation is cached by cruncher
2025-07-02 05:52:02.071 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:02.080 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:02.088 cruncher.ratio() > best_ratio:
2025-07-02 05:52:02.095 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:02.102 if best_ratio < cutoff:
2025-07-02 05:52:02.109 # no non-identical "pretty close" pair
2025-07-02 05:52:02.119 if eqi is None:
2025-07-02 05:52:02.129 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:02.136 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:02.141 return
2025-07-02 05:52:02.147 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:02.155 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:02.163 else:
2025-07-02 05:52:02.170 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:02.180 eqi = None
2025-07-02 05:52:02.188
2025-07-02 05:52:02.195 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:02.203 # identical
2025-07-02 05:52:02.213
2025-07-02 05:52:02.221 # pump out diffs from before the synch point
2025-07-02 05:52:02.228 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:02.234
2025-07-02 05:52:02.243 # do intraline marking on the synch pair
2025-07-02 05:52:02.256 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:02.266 if eqi is None:
2025-07-02 05:52:02.279 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:02.288 atags = btags = ""
2025-07-02 05:52:02.296 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:02.303 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:02.318 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:02.327 if tag == 'replace':
2025-07-02 05:52:02.334 atags += '^' * la
2025-07-02 05:52:02.341 btags += '^' * lb
2025-07-02 05:52:02.348 elif tag == 'delete':
2025-07-02 05:52:02.355 atags += '-' * la
2025-07-02 05:52:02.363 elif tag == 'insert':
2025-07-02 05:52:02.378 btags += '+' * lb
2025-07-02 05:52:02.390 elif tag == 'equal':
2025-07-02 05:52:02.399 atags += ' ' * la
2025-07-02 05:52:02.409 btags += ' ' * lb
2025-07-02 05:52:02.420 else:
2025-07-02 05:52:02.433 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:02.442 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:02.456 else:
2025-07-02 05:52:02.465 # the synch pair is identical
2025-07-02 05:52:02.473 yield '  ' + aelt
2025-07-02 05:52:02.479
2025-07-02 05:52:02.484 # pump out diffs from after the synch point
2025-07-02 05:52:02.490 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:02.496
2025-07-02 05:52:02.502 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:02.509 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:02.516
2025-07-02 05:52:02.524 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:02.536 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:02.546 alo = 415, ahi = 1101
2025-07-02 05:52:02.554 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:02.559 blo = 415, bhi = 1101
2025-07-02 05:52:02.565
2025-07-02 05:52:02.570 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:02.575 g = []
2025-07-02 05:52:02.580 if alo < ahi:
2025-07-02 05:52:02.586 if blo < bhi:
2025-07-02 05:52:02.592 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:02.598 else:
2025-07-02 05:52:02.605 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:02.612 elif blo < bhi:
2025-07-02 05:52:02.618 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:02.626
2025-07-02 05:52:02.637 >       yield from g
2025-07-02 05:52:02.647
2025-07-02 05:52:02.655 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:02.662 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:02.668
2025-07-02 05:52:02.675 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:02.682 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:02.689 alo = 415, ahi = 1101
2025-07-02 05:52:02.702 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:02.712 blo = 415, bhi = 1101
2025-07-02 05:52:02.721
2025-07-02 05:52:02.729 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:02.736 r"""
2025-07-02 05:52:02.741 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:02.746 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:02.753 synch point, and intraline difference marking is done on the
2025-07-02 05:52:02.760 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:02.766
2025-07-02 05:52:02.773 Example:
2025-07-02 05:52:02.784
2025-07-02 05:52:02.794 >>> d = Differ()
2025-07-02 05:52:02.803 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:02.810 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:02.816 >>> print(''.join(results), end="")
2025-07-02 05:52:02.822 - abcDefghiJkl
2025-07-02 05:52:02.835 + abcdefGhijkl
2025-07-02 05:52:02.847 """
2025-07-02 05:52:02.854
2025-07-02 05:52:02.867 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:02.876 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:02.885 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:02.894 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:02.903 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:02.911
2025-07-02 05:52:02.919 # search for the pair that matches best without being identical
2025-07-02 05:52:02.925 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:02.931 # on junk -- unless we have to)
2025-07-02 05:52:02.939 for j in range(blo, bhi):
2025-07-02 05:52:02.946 bj = b[j]
2025-07-02 05:52:02.953 cruncher.set_seq2(bj)
2025-07-02 05:52:02.961 for i in range(alo, ahi):
2025-07-02 05:52:02.968 ai = a[i]
2025-07-02 05:52:02.975 if ai == bj:
2025-07-02 05:52:02.981 if eqi is None:
2025-07-02 05:52:02.987 eqi, eqj = i, j
2025-07-02 05:52:02.993 continue
2025-07-02 05:52:03.000 cruncher.set_seq1(ai)
2025-07-02 05:52:03.011 # computing similarity is expensive, so use the quick
2025-07-02 05:52:03.022 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:03.031 # compares by a factor of 3.
2025-07-02 05:52:03.042 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:03.051 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:03.063 # of the computation is cached by cruncher
2025-07-02 05:52:03.074 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:03.083 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:03.092 cruncher.ratio() > best_ratio:
2025-07-02 05:52:03.104 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:03.111 if best_ratio < cutoff:
2025-07-02 05:52:03.118 # no non-identical "pretty close" pair
2025-07-02 05:52:03.127 if eqi is None:
2025-07-02 05:52:03.138 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:03.146 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:03.153 return
2025-07-02 05:52:03.164 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:03.178 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:03.188 else:
2025-07-02 05:52:03.201 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:03.211 eqi = None
2025-07-02 05:52:03.217
2025-07-02 05:52:03.225 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:03.232 # identical
2025-07-02 05:52:03.239
2025-07-02 05:52:03.245 # pump out diffs from before the synch point
2025-07-02 05:52:03.251 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:03.257
2025-07-02 05:52:03.265 # do intraline marking on the synch pair
2025-07-02 05:52:03.274 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:03.280 if eqi is None:
2025-07-02 05:52:03.286 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:03.297 atags = btags = ""
2025-07-02 05:52:03.307 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:03.314 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:03.320 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:03.327 if tag == 'replace':
2025-07-02 05:52:03.332 atags += '^' * la
2025-07-02 05:52:03.343 btags += '^' * lb
2025-07-02 05:52:03.356 elif tag == 'delete':
2025-07-02 05:52:03.365 atags += '-' * la
2025-07-02 05:52:03.376 elif tag == 'insert':
2025-07-02 05:52:03.387 btags += '+' * lb
2025-07-02 05:52:03.396 elif tag == 'equal':
2025-07-02 05:52:03.403 atags += ' ' * la
2025-07-02 05:52:03.417 btags += ' ' * lb
2025-07-02 05:52:03.427 else:
2025-07-02 05:52:03.438 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:03.447 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:03.457 else:
2025-07-02 05:52:03.466 # the synch pair is identical
2025-07-02 05:52:03.475 yield '  ' + aelt
2025-07-02 05:52:03.483
2025-07-02 05:52:03.496 # pump out diffs from after the synch point
2025-07-02 05:52:03.507 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:03.517
2025-07-02 05:52:03.529 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:03.539 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:03.548
2025-07-02 05:52:03.556 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:03.564 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:03.571 alo = 416, ahi = 1101
2025-07-02 05:52:03.578 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:03.590 blo = 416, bhi = 1101
2025-07-02 05:52:03.603
2025-07-02 05:52:03.614 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:03.624 g = []
2025-07-02 05:52:03.633 if alo < ahi:
2025-07-02 05:52:03.643 if blo < bhi:
2025-07-02 05:52:03.651 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:03.659 else:
2025-07-02 05:52:03.666 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:03.672 elif blo < bhi:
2025-07-02 05:52:03.678 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:03.684
2025-07-02 05:52:03.691 >       yield from g
2025-07-02 05:52:03.696
2025-07-02 05:52:03.703 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:03.709 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:03.715
2025-07-02 05:52:03.725 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:03.741 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:03.749 alo = 416, ahi = 1101
2025-07-02 05:52:03.757 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:03.763 blo = 416, bhi = 1101
2025-07-02 05:52:03.772
2025-07-02 05:52:03.782 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:03.789 r"""
2025-07-02 05:52:03.796 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:03.803 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:03.810 synch point, and intraline difference marking is done on the
2025-07-02 05:52:03.819 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:03.831
2025-07-02 05:52:03.840 Example:
2025-07-02 05:52:03.848
2025-07-02 05:52:03.857 >>> d = Differ()
2025-07-02 05:52:03.873 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:03.883 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:03.889 >>> print(''.join(results), end="")
2025-07-02 05:52:03.895 - abcDefghiJkl
2025-07-02 05:52:03.916 + abcdefGhijkl
2025-07-02 05:52:03.942 """
2025-07-02 05:52:03.953
2025-07-02 05:52:03.965 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:03.974 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:03.984 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:03.992 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:03.998 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:04.004
2025-07-02 05:52:04.010 # search for the pair that matches best without being identical
2025-07-02 05:52:04.016 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:04.022 # on junk -- unless we have to)
2025-07-02 05:52:04.027 for j in range(blo, bhi):
2025-07-02 05:52:04.039 bj = b[j]
2025-07-02 05:52:04.050 cruncher.set_seq2(bj)
2025-07-02 05:52:04.060 for i in range(alo, ahi):
2025-07-02 05:52:04.067 ai = a[i]
2025-07-02 05:52:04.074 if ai == bj:
2025-07-02 05:52:04.084 if eqi is None:
2025-07-02 05:52:04.094 eqi, eqj = i, j
2025-07-02 05:52:04.102 continue
2025-07-02 05:52:04.110 cruncher.set_seq1(ai)
2025-07-02 05:52:04.119 # computing similarity is expensive, so use the quick
2025-07-02 05:52:04.131 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:04.142 # compares by a factor of 3.
2025-07-02 05:52:04.154 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:04.164 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:04.172 # of the computation is cached by cruncher
2025-07-02 05:52:04.180 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:04.187 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:04.193 cruncher.ratio() > best_ratio:
2025-07-02 05:52:04.207 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:04.219 if best_ratio < cutoff:
2025-07-02 05:52:04.228 # no non-identical "pretty close" pair
2025-07-02 05:52:04.236 if eqi is None:
2025-07-02 05:52:04.248 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:04.260 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:04.271 return
2025-07-02 05:52:04.282 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:04.297 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:04.310 else:
2025-07-02 05:52:04.322 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:04.331 eqi = None
2025-07-02 05:52:04.339
2025-07-02 05:52:04.346 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:04.352 # identical
2025-07-02 05:52:04.358
2025-07-02 05:52:04.366 # pump out diffs from before the synch point
2025-07-02 05:52:04.379 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:04.389
2025-07-02 05:52:04.397 # do intraline marking on the synch pair
2025-07-02 05:52:04.403 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:04.410 if eqi is None:
2025-07-02 05:52:04.416 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:04.423 atags = btags = ""
2025-07-02 05:52:04.436 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:04.447 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:04.459 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:04.473 if tag == 'replace':
2025-07-02 05:52:04.483 atags += '^' * la
2025-07-02 05:52:04.493 btags += '^' * lb
2025-07-02 05:52:04.501 elif tag == 'delete':
2025-07-02 05:52:04.509 atags += '-' * la
2025-07-02 05:52:04.516 elif tag == 'insert':
2025-07-02 05:52:04.527 btags += '+' * lb
2025-07-02 05:52:04.536 elif tag == 'equal':
2025-07-02 05:52:04.544 atags += ' ' * la
2025-07-02 05:52:04.551 btags += ' ' * lb
2025-07-02 05:52:04.558 else:
2025-07-02 05:52:04.570 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:04.579 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:04.587 else:
2025-07-02 05:52:04.594 # the synch pair is identical
2025-07-02 05:52:04.606 yield '  ' + aelt
2025-07-02 05:52:04.616
2025-07-02 05:52:04.624 # pump out diffs from after the synch point
2025-07-02 05:52:04.630 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:04.637
2025-07-02 05:52:04.646 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:04.657 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:04.671
2025-07-02 05:52:04.679 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:04.688 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:04.704 alo = 417, ahi = 1101
2025-07-02 05:52:04.714 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:04.721 blo = 417, bhi = 1101
2025-07-02 05:52:04.727
2025-07-02 05:52:04.734 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:04.743 g = []
2025-07-02 05:52:04.749 if alo < ahi:
2025-07-02 05:52:04.755 if blo < bhi:
2025-07-02 05:52:04.761 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:04.767 else:
2025-07-02 05:52:04.774 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:04.782 elif blo < bhi:
2025-07-02 05:52:04.790 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:04.797
2025-07-02 05:52:04.804 >       yield from g
2025-07-02 05:52:04.810
2025-07-02 05:52:04.816 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:04.829 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:04.838
2025-07-02 05:52:04.843 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:04.849 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:04.854 alo = 417, ahi = 1101
2025-07-02 05:52:04.863 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:04.872 blo = 417, bhi = 1101
2025-07-02 05:52:04.879
2025-07-02 05:52:04.887 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:04.898 r"""
2025-07-02 05:52:04.908 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:04.919 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:04.931 synch point, and intraline difference marking is done on the
2025-07-02 05:52:04.939 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:04.945
2025-07-02 05:52:04.954 Example:
2025-07-02 05:52:04.960
2025-07-02 05:52:04.973 >>> d = Differ()
2025-07-02 05:52:04.984 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:04.993 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:05.000 >>> print(''.join(results), end="")
2025-07-02 05:52:05.011 - abcDefghiJkl
2025-07-02 05:52:05.035 + abcdefGhijkl
2025-07-02 05:52:05.061 """
2025-07-02 05:52:05.072
2025-07-02 05:52:05.083 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:05.091 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:05.099 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:05.106 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:05.117 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:05.124
2025-07-02 05:52:05.131 # search for the pair that matches best without being identical
2025-07-02 05:52:05.138 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:05.144 # on junk -- unless we have to)
2025-07-02 05:52:05.151 for j in range(blo, bhi):
2025-07-02 05:52:05.160 bj = b[j]
2025-07-02 05:52:05.169 cruncher.set_seq2(bj)
2025-07-02 05:52:05.177 for i in range(alo, ahi):
2025-07-02 05:52:05.182 ai = a[i]
2025-07-02 05:52:05.189 if ai == bj:
2025-07-02 05:52:05.200 if eqi is None:
2025-07-02 05:52:05.210 eqi, eqj = i, j
2025-07-02 05:52:05.219 continue
2025-07-02 05:52:05.226 cruncher.set_seq1(ai)
2025-07-02 05:52:05.234 # computing similarity is expensive, so use the quick
2025-07-02 05:52:05.246 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:05.260 # compares by a factor of 3.
2025-07-02 05:52:05.273 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:05.285 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:05.292 # of the computation is cached by cruncher
2025-07-02 05:52:05.302 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:05.315 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:05.327 cruncher.ratio() > best_ratio:
2025-07-02 05:52:05.336 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:05.345 if best_ratio < cutoff:
2025-07-02 05:52:05.351 # no non-identical "pretty close" pair
2025-07-02 05:52:05.358 if eqi is None:
2025-07-02 05:52:05.366 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:05.374 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:05.380 return
2025-07-02 05:52:05.390 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:05.400 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:05.406 else:
2025-07-02 05:52:05.414 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:05.422 eqi = None
2025-07-02 05:52:05.430
2025-07-02 05:52:05.444 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:05.455 # identical
2025-07-02 05:52:05.464
2025-07-02 05:52:05.472 # pump out diffs from before the synch point
2025-07-02 05:52:05.479 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:05.488
2025-07-02 05:52:05.502 # do intraline marking on the synch pair
2025-07-02 05:52:05.512 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:05.518 if eqi is None:
2025-07-02 05:52:05.532 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:05.541 atags = btags = ""
2025-07-02 05:52:05.551 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:05.558 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:05.565 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:05.572 if tag == 'replace':
2025-07-02 05:52:05.578 atags += '^' * la
2025-07-02 05:52:05.587 btags += '^' * lb
2025-07-02 05:52:05.595 elif tag == 'delete':
2025-07-02 05:52:05.607 atags += '-' * la
2025-07-02 05:52:05.616 elif tag == 'insert':
2025-07-02 05:52:05.623 btags += '+' * lb
2025-07-02 05:52:05.630 elif tag == 'equal':
2025-07-02 05:52:05.638 atags += ' ' * la
2025-07-02 05:52:05.643 btags += ' ' * lb
2025-07-02 05:52:05.649 else:
2025-07-02 05:52:05.655 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:05.661 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:05.667 else:
2025-07-02 05:52:05.674 # the synch pair is identical
2025-07-02 05:52:05.682 yield '  ' + aelt
2025-07-02 05:52:05.690
2025-07-02 05:52:05.698 # pump out diffs from after the synch point
2025-07-02 05:52:05.707 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:05.714
2025-07-02 05:52:05.725 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:05.736 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:05.746
2025-07-02 05:52:05.758 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:05.768 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:05.780 alo = 418, ahi = 1101
2025-07-02 05:52:05.792 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:05.802 blo = 418, bhi = 1101
2025-07-02 05:52:05.812
2025-07-02 05:52:05.822 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:05.832 g = []
2025-07-02 05:52:05.840 if alo < ahi:
2025-07-02 05:52:05.847 if blo < bhi:
2025-07-02 05:52:05.855 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:05.862 else:
2025-07-02 05:52:05.871 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:05.880 elif blo < bhi:
2025-07-02 05:52:05.887 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:05.894
2025-07-02 05:52:05.901 >       yield from g
2025-07-02 05:52:05.908
2025-07-02 05:52:05.914 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:05.923 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:05.930
2025-07-02 05:52:05.937 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:05.942 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:05.947 alo = 418, ahi = 1101
2025-07-02 05:52:05.954 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:05.960 blo = 418, bhi = 1101
2025-07-02 05:52:05.967
2025-07-02 05:52:05.975 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:05.982 r"""
2025-07-02 05:52:05.990 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:05.999 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:06.010 synch point, and intraline difference marking is done on the
2025-07-02 05:52:06.019 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:06.026
2025-07-02 05:52:06.033 Example:
2025-07-02 05:52:06.039
2025-07-02 05:52:06.046 >>> d = Differ()
2025-07-02 05:52:06.053 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:06.065 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:06.074 >>> print(''.join(results), end="")
2025-07-02 05:52:06.082 - abcDefghiJkl
2025-07-02 05:52:06.095 + abcdefGhijkl
2025-07-02 05:52:06.108 """
2025-07-02 05:52:06.115
2025-07-02 05:52:06.122 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:06.129 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:06.136 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:06.144 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:06.151 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:06.158
2025-07-02 05:52:06.171 # search for the pair that matches best without being identical
2025-07-02 05:52:06.179 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:06.188 # on junk -- unless we have to)
2025-07-02 05:52:06.195 for j in range(blo, bhi):
2025-07-02 05:52:06.210 bj = b[j]
2025-07-02 05:52:06.221 cruncher.set_seq2(bj)
2025-07-02 05:52:06.228 for i in range(alo, ahi):
2025-07-02 05:52:06.235 ai = a[i]
2025-07-02 05:52:06.241 if ai == bj:
2025-07-02 05:52:06.247 if eqi is None:
2025-07-02 05:52:06.253 eqi, eqj = i, j
2025-07-02 05:52:06.259 continue
2025-07-02 05:52:06.267 cruncher.set_seq1(ai)
2025-07-02 05:52:06.278 # computing similarity is expensive, so use the quick
2025-07-02 05:52:06.286 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:06.293 # compares by a factor of 3.
2025-07-02 05:52:06.303 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:06.316 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:06.329 # of the computation is cached by cruncher
2025-07-02 05:52:06.339 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:06.347 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:06.355 cruncher.ratio() > best_ratio:
2025-07-02 05:52:06.362 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:06.369 if best_ratio < cutoff:
2025-07-02 05:52:06.378 # no non-identical "pretty close" pair
2025-07-02 05:52:06.383 if eqi is None:
2025-07-02 05:52:06.394 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:06.401 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:06.406 return
2025-07-02 05:52:06.413 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:06.418 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:06.428 else:
2025-07-02 05:52:06.439 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:06.447 eqi = None
2025-07-02 05:52:06.457
2025-07-02 05:52:06.466 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:06.475 # identical
2025-07-02 05:52:06.482
2025-07-02 05:52:06.488 # pump out diffs from before the synch point
2025-07-02 05:52:06.501 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:06.512
2025-07-02 05:52:06.519 # do intraline marking on the synch pair
2025-07-02 05:52:06.526 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:06.531 if eqi is None:
2025-07-02 05:52:06.540 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:06.548 atags = btags = ""
2025-07-02 05:52:06.555 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:06.567 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:06.579 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:06.590 if tag == 'replace':
2025-07-02 05:52:06.599 atags += '^' * la
2025-07-02 05:52:06.605 btags += '^' * lb
2025-07-02 05:52:06.611 elif tag == 'delete':
2025-07-02 05:52:06.617 atags += '-' * la
2025-07-02 05:52:06.624 elif tag == 'insert':
2025-07-02 05:52:06.631 btags += '+' * lb
2025-07-02 05:52:06.638 elif tag == 'equal':
2025-07-02 05:52:06.646 atags += ' ' * la
2025-07-02 05:52:06.654 btags += ' ' * lb
2025-07-02 05:52:06.662 else:
2025-07-02 05:52:06.669 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:06.676 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:06.683 else:
2025-07-02 05:52:06.690 # the synch pair is identical
2025-07-02 05:52:06.698 yield '  ' + aelt
2025-07-02 05:52:06.705
2025-07-02 05:52:06.712 # pump out diffs from after the synch point
2025-07-02 05:52:06.719 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:06.726
2025-07-02 05:52:06.732 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:06.739 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:06.745
2025-07-02 05:52:06.750 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:06.757 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:06.763 alo = 419, ahi = 1101
2025-07-02 05:52:06.771 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:06.778 blo = 419, bhi = 1101
2025-07-02 05:52:06.785
2025-07-02 05:52:06.792 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:06.800 g = []
2025-07-02 05:52:06.807 if alo < ahi:
2025-07-02 05:52:06.815 if blo < bhi:
2025-07-02 05:52:06.823 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:06.831 else:
2025-07-02 05:52:06.839 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:06.845 elif blo < bhi:
2025-07-02 05:52:06.852 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:06.858
2025-07-02 05:52:06.864 >       yield from g
2025-07-02 05:52:06.870
2025-07-02 05:52:06.877 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:06.883 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:06.888
2025-07-02 05:52:06.893 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:06.901 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:06.911 alo = 419, ahi = 1101
2025-07-02 05:52:06.923 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:06.937 blo = 419, bhi = 1101
2025-07-02 05:52:06.948
2025-07-02 05:52:06.962 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:06.972 r"""
2025-07-02 05:52:06.981 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:06.988 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:07.001 synch point, and intraline difference marking is done on the
2025-07-02 05:52:07.013 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:07.026
2025-07-02 05:52:07.040 Example:
2025-07-02 05:52:07.052
2025-07-02 05:52:07.065 >>> d = Differ()
2025-07-02 05:52:07.076 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:07.085 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:07.092 >>> print(''.join(results), end="")
2025-07-02 05:52:07.098 - abcDefghiJkl
2025-07-02 05:52:07.118 + abcdefGhijkl
2025-07-02 05:52:07.135 """
2025-07-02 05:52:07.141
2025-07-02 05:52:07.148 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:07.154 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:07.160 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:07.166 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:07.173 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:07.180
2025-07-02 05:52:07.187 # search for the pair that matches best without being identical
2025-07-02 05:52:07.193 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:07.198 # on junk -- unless we have to)
2025-07-02 05:52:07.207 for j in range(blo, bhi):
2025-07-02 05:52:07.216 bj = b[j]
2025-07-02 05:52:07.224 cruncher.set_seq2(bj)
2025-07-02 05:52:07.231 for i in range(alo, ahi):
2025-07-02 05:52:07.237 ai = a[i]
2025-07-02 05:52:07.242 if ai == bj:
2025-07-02 05:52:07.250 if eqi is None:
2025-07-02 05:52:07.258 eqi, eqj = i, j
2025-07-02 05:52:07.264 continue
2025-07-02 05:52:07.272 cruncher.set_seq1(ai)
2025-07-02 05:52:07.284 # computing similarity is expensive, so use the quick
2025-07-02 05:52:07.295 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:07.305 # compares by a factor of 3.
2025-07-02 05:52:07.320 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:07.334 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:07.342 # of the computation is cached by cruncher
2025-07-02 05:52:07.350 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:07.361 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:07.374 cruncher.ratio() > best_ratio:
2025-07-02 05:52:07.383 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:07.392 if best_ratio < cutoff:
2025-07-02 05:52:07.398 # no non-identical "pretty close" pair
2025-07-02 05:52:07.404 if eqi is None:
2025-07-02 05:52:07.409 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:07.414 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:07.418 return
2025-07-02 05:52:07.423 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:07.428 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:07.433 else:
2025-07-02 05:52:07.438 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:07.443 eqi = None
2025-07-02 05:52:07.447
2025-07-02 05:52:07.452 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:07.457 # identical
2025-07-02 05:52:07.461
2025-07-02 05:52:07.466 # pump out diffs from before the synch point
2025-07-02 05:52:07.471 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:07.476
2025-07-02 05:52:07.480 # do intraline marking on the synch pair
2025-07-02 05:52:07.485 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:07.490 if eqi is None:
2025-07-02 05:52:07.494 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:07.499 atags = btags = ""
2025-07-02 05:52:07.504 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:07.509 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:07.513 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:07.518 if tag == 'replace':
2025-07-02 05:52:07.523 atags += '^' * la
2025-07-02 05:52:07.527 btags += '^' * lb
2025-07-02 05:52:07.532 elif tag == 'delete':
2025-07-02 05:52:07.538 atags += '-' * la
2025-07-02 05:52:07.543 elif tag == 'insert':
2025-07-02 05:52:07.547 btags += '+' * lb
2025-07-02 05:52:07.552 elif tag == 'equal':
2025-07-02 05:52:07.557 atags += ' ' * la
2025-07-02 05:52:07.562 btags += ' ' * lb
2025-07-02 05:52:07.567 else:
2025-07-02 05:52:07.573 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:07.585 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:07.594 else:
2025-07-02 05:52:07.601 # the synch pair is identical
2025-07-02 05:52:07.608 yield '  ' + aelt
2025-07-02 05:52:07.613
2025-07-02 05:52:07.619 # pump out diffs from after the synch point
2025-07-02 05:52:07.626 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:07.632
2025-07-02 05:52:07.638 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:07.649 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:07.659
2025-07-02 05:52:07.668 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:07.678 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:07.685 alo = 422, ahi = 1101
2025-07-02 05:52:07.695 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:07.708 blo = 422, bhi = 1101
2025-07-02 05:52:07.716
2025-07-02 05:52:07.723 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:07.730 g = []
2025-07-02 05:52:07.739 if alo < ahi:
2025-07-02 05:52:07.745 if blo < bhi:
2025-07-02 05:52:07.751 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:07.757 else:
2025-07-02 05:52:07.763 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:07.769 elif blo < bhi:
2025-07-02 05:52:07.775 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:07.782
2025-07-02 05:52:07.789 >       yield from g
2025-07-02 05:52:07.795
2025-07-02 05:52:07.800 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:07.806 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:07.811
2025-07-02 05:52:07.819 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:07.831 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:07.838 alo = 422, ahi = 1101
2025-07-02 05:52:07.847 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:07.857 blo = 422, bhi = 1101
2025-07-02 05:52:07.866
2025-07-02 05:52:07.881 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:07.892 r"""
2025-07-02 05:52:07.900 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:07.908 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:07.914 synch point, and intraline difference marking is done on the
2025-07-02 05:52:07.920 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:07.926
2025-07-02 05:52:07.934 Example:
2025-07-02 05:52:07.942
2025-07-02 05:52:07.948 >>> d = Differ()
2025-07-02 05:52:07.955 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:07.962 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:07.971 >>> print(''.join(results), end="")
2025-07-02 05:52:07.982 - abcDefghiJkl
2025-07-02 05:52:08.004 + abcdefGhijkl
2025-07-02 05:52:08.024 """
2025-07-02 05:52:08.033
2025-07-02 05:52:08.041 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:08.050 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:08.061 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:08.073 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:08.084 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:08.092
2025-07-02 05:52:08.099 # search for the pair that matches best without being identical
2025-07-02 05:52:08.107 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:08.114 # on junk -- unless we have to)
2025-07-02 05:52:08.120 for j in range(blo, bhi):
2025-07-02 05:52:08.126 bj = b[j]
2025-07-02 05:52:08.132 cruncher.set_seq2(bj)
2025-07-02 05:52:08.141 for i in range(alo, ahi):
2025-07-02 05:52:08.151 ai = a[i]
2025-07-02 05:52:08.161 if ai == bj:
2025-07-02 05:52:08.172 if eqi is None:
2025-07-02 05:52:08.183 eqi, eqj = i, j
2025-07-02 05:52:08.193 continue
2025-07-02 05:52:08.203 cruncher.set_seq1(ai)
2025-07-02 05:52:08.215 # computing similarity is expensive, so use the quick
2025-07-02 05:52:08.223 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:08.232 # compares by a factor of 3.
2025-07-02 05:52:08.239 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:08.248 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:08.261 # of the computation is cached by cruncher
2025-07-02 05:52:08.271 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:08.280 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:08.293 cruncher.ratio() > best_ratio:
2025-07-02 05:52:08.306 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:08.315 if best_ratio < cutoff:
2025-07-02 05:52:08.323 # no non-identical "pretty close" pair
2025-07-02 05:52:08.331 if eqi is None:
2025-07-02 05:52:08.338 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:08.346 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:08.357 return
2025-07-02 05:52:08.368 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:08.377 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:08.384 else:
2025-07-02 05:52:08.390 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:08.400 eqi = None
2025-07-02 05:52:08.411
2025-07-02 05:52:08.419 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:08.427 # identical
2025-07-02 05:52:08.434
2025-07-02 05:52:08.445 # pump out diffs from before the synch point
2025-07-02 05:52:08.453 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:08.459
2025-07-02 05:52:08.465 # do intraline marking on the synch pair
2025-07-02 05:52:08.471 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:08.476 if eqi is None:
2025-07-02 05:52:08.482 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:08.489 atags = btags = ""
2025-07-02 05:52:08.495 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:08.500 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:08.506 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:08.512 if tag == 'replace':
2025-07-02 05:52:08.517 atags += '^' * la
2025-07-02 05:52:08.522 btags += '^' * lb
2025-07-02 05:52:08.529 elif tag == 'delete':
2025-07-02 05:52:08.542 atags += '-' * la
2025-07-02 05:52:08.555 elif tag == 'insert':
2025-07-02 05:52:08.567 btags += '+' * lb
2025-07-02 05:52:08.577 elif tag == 'equal':
2025-07-02 05:52:08.590 atags += ' ' * la
2025-07-02 05:52:08.598 btags += ' ' * lb
2025-07-02 05:52:08.605 else:
2025-07-02 05:52:08.612 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:08.620 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:08.626 else:
2025-07-02 05:52:08.631 # the synch pair is identical
2025-07-02 05:52:08.637 yield '  ' + aelt
2025-07-02 05:52:08.642
2025-07-02 05:52:08.647 # pump out diffs from after the synch point
2025-07-02 05:52:08.655 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:08.668
2025-07-02 05:52:08.676 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:08.683 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:08.690
2025-07-02 05:52:08.697 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:08.704 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:08.710 alo = 423, ahi = 1101
2025-07-02 05:52:08.715 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:08.723 blo = 423, bhi = 1101
2025-07-02 05:52:08.731
2025-07-02 05:52:08.739 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:08.747 g = []
2025-07-02 05:52:08.755 if alo < ahi:
2025-07-02 05:52:08.762 if blo < bhi:
2025-07-02 05:52:08.771 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:08.778 else:
2025-07-02 05:52:08.788 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:08.801 elif blo < bhi:
2025-07-02 05:52:08.813 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:08.824
2025-07-02 05:52:08.833 >       yield from g
2025-07-02 05:52:08.840
2025-07-02 05:52:08.848 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:08.855 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:08.864
2025-07-02 05:52:08.875 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:08.884 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:08.891 alo = 423, ahi = 1101
2025-07-02 05:52:08.897 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:08.903 blo = 423, bhi = 1101
2025-07-02 05:52:08.910
2025-07-02 05:52:08.916 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:08.928 r"""
2025-07-02 05:52:08.937 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:08.945 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:08.957 synch point, and intraline difference marking is done on the
2025-07-02 05:52:08.968 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:08.976
2025-07-02 05:52:08.983 Example:
2025-07-02 05:52:08.988
2025-07-02 05:52:08.994 >>> d = Differ()
2025-07-02 05:52:08.999 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:09.007 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:09.019 >>> print(''.join(results), end="")
2025-07-02 05:52:09.027 - abcDefghiJkl
2025-07-02 05:52:09.040 + abcdefGhijkl
2025-07-02 05:52:09.067 """
2025-07-02 05:52:09.078
2025-07-02 05:52:09.087 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:09.095 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:09.102 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:09.114 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:09.124 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:09.131
2025-07-02 05:52:09.138 # search for the pair that matches best without being identical
2025-07-02 05:52:09.146 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:09.153 # on junk -- unless we have to)
2025-07-02 05:52:09.159 for j in range(blo, bhi):
2025-07-02 05:52:09.165 bj = b[j]
2025-07-02 05:52:09.172 cruncher.set_seq2(bj)
2025-07-02 05:52:09.179 for i in range(alo, ahi):
2025-07-02 05:52:09.185 ai = a[i]
2025-07-02 05:52:09.190 if ai == bj:
2025-07-02 05:52:09.200 if eqi is None:
2025-07-02 05:52:09.210 eqi, eqj = i, j
2025-07-02 05:52:09.218 continue
2025-07-02 05:52:09.227 cruncher.set_seq1(ai)
2025-07-02 05:52:09.239 # computing similarity is expensive, so use the quick
2025-07-02 05:52:09.248 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:09.255 # compares by a factor of 3.
2025-07-02 05:52:09.262 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:09.271 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:09.282 # of the computation is cached by cruncher
2025-07-02 05:52:09.291 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:09.297 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:09.304 cruncher.ratio() > best_ratio:
2025-07-02 05:52:09.310 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:09.320 if best_ratio < cutoff:
2025-07-02 05:52:09.329 # no non-identical "pretty close" pair
2025-07-02 05:52:09.337 if eqi is None:
2025-07-02 05:52:09.344 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:09.351 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:09.361 return
2025-07-02 05:52:09.374 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:09.387 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:09.398 else:
2025-07-02 05:52:09.408 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:09.422 eqi = None
2025-07-02 05:52:09.434
2025-07-02 05:52:09.445 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:09.459 # identical
2025-07-02 05:52:09.473
2025-07-02 05:52:09.485 # pump out diffs from before the synch point
2025-07-02 05:52:09.496 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:09.507
2025-07-02 05:52:09.518 # do intraline marking on the synch pair
2025-07-02 05:52:09.530 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:09.540 if eqi is None:
2025-07-02 05:52:09.549 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:09.560 atags = btags = ""
2025-07-02 05:52:09.570 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:09.579 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:09.588 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:09.596 if tag == 'replace':
2025-07-02 05:52:09.603 atags += '^' * la
2025-07-02 05:52:09.610 btags += '^' * lb
2025-07-02 05:52:09.616 elif tag == 'delete':
2025-07-02 05:52:09.622 atags += '-' * la
2025-07-02 05:52:09.629 elif tag == 'insert':
2025-07-02 05:52:09.633 btags += '+' * lb
2025-07-02 05:52:09.638 elif tag == 'equal':
2025-07-02 05:52:09.643 atags += ' ' * la
2025-07-02 05:52:09.647 btags += ' ' * lb
2025-07-02 05:52:09.652 else:
2025-07-02 05:52:09.657 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:09.661 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:09.666 else:
2025-07-02 05:52:09.671 # the synch pair is identical
2025-07-02 05:52:09.676 yield '  ' + aelt
2025-07-02 05:52:09.681
2025-07-02 05:52:09.686 # pump out diffs from after the synch point
2025-07-02 05:52:09.690 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:09.695
2025-07-02 05:52:09.700 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:09.705 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:09.710
2025-07-02 05:52:09.714 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:09.720 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:09.724 alo = 424, ahi = 1101
2025-07-02 05:52:09.730 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:09.735 blo = 424, bhi = 1101
2025-07-02 05:52:09.739
2025-07-02 05:52:09.748 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:09.756 g = []
2025-07-02 05:52:09.762 if alo < ahi:
2025-07-02 05:52:09.770 if blo < bhi:
2025-07-02 05:52:09.777 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:09.785 else:
2025-07-02 05:52:09.791 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:09.801 elif blo < bhi:
2025-07-02 05:52:09.810 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:09.818
2025-07-02 05:52:09.826 >       yield from g
2025-07-02 05:52:09.832
2025-07-02 05:52:09.838 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:09.851 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:09.862
2025-07-02 05:52:09.873 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:09.881 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:09.888 alo = 424, ahi = 1101
2025-07-02 05:52:09.896 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:09.902 blo = 424, bhi = 1101
2025-07-02 05:52:09.909
2025-07-02 05:52:09.916 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:09.923 r"""
2025-07-02 05:52:09.930 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:09.938 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:09.948 synch point, and intraline difference marking is done on the
2025-07-02 05:52:09.961 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:09.972
2025-07-02 05:52:09.980 Example:
2025-07-02 05:52:09.987
2025-07-02 05:52:09.993 >>> d = Differ()
2025-07-02 05:52:09.998 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:10.006 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:10.012 >>> print(''.join(results), end="")
2025-07-02 05:52:10.018 - abcDefghiJkl
2025-07-02 05:52:10.041 + abcdefGhijkl
2025-07-02 05:52:10.059 """
2025-07-02 05:52:10.071
2025-07-02 05:52:10.079 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:10.087 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:10.094 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:10.106 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:10.119 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:10.131
2025-07-02 05:52:10.143 # search for the pair that matches best without being identical
2025-07-02 05:52:10.151 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:10.158 # on junk -- unless we have to)
2025-07-02 05:52:10.168 for j in range(blo, bhi):
2025-07-02 05:52:10.177 bj = b[j]
2025-07-02 05:52:10.185 cruncher.set_seq2(bj)
2025-07-02 05:52:10.192 for i in range(alo, ahi):
2025-07-02 05:52:10.199 ai = a[i]
2025-07-02 05:52:10.209 if ai == bj:
2025-07-02 05:52:10.216 if eqi is None:
2025-07-02 05:52:10.222 eqi, eqj = i, j
2025-07-02 05:52:10.228 continue
2025-07-02 05:52:10.239 cruncher.set_seq1(ai)
2025-07-02 05:52:10.248 # computing similarity is expensive, so use the quick
2025-07-02 05:52:10.256 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:10.262 # compares by a factor of 3.
2025-07-02 05:52:10.268 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:10.275 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:10.283 # of the computation is cached by cruncher
2025-07-02 05:52:10.292 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:10.298 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:10.305 cruncher.ratio() > best_ratio:
2025-07-02 05:52:10.311 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:10.318 if best_ratio < cutoff:
2025-07-02 05:52:10.324 # no non-identical "pretty close" pair
2025-07-02 05:52:10.329 if eqi is None:
2025-07-02 05:52:10.336 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:10.343 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:10.351 return
2025-07-02 05:52:10.364 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:10.373 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:10.383 else:
2025-07-02 05:52:10.391 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:10.398 eqi = None
2025-07-02 05:52:10.410
2025-07-02 05:52:10.419 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:10.427 # identical
2025-07-02 05:52:10.434
2025-07-02 05:52:10.443 # pump out diffs from before the synch point
2025-07-02 05:52:10.449 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:10.457
2025-07-02 05:52:10.464 # do intraline marking on the synch pair
2025-07-02 05:52:10.472 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:10.479 if eqi is None:
2025-07-02 05:52:10.486 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:10.492 atags = btags = ""
2025-07-02 05:52:10.500 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:10.507 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:10.514 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:10.527 if tag == 'replace':
2025-07-02 05:52:10.537 atags += '^' * la
2025-07-02 05:52:10.546 btags += '^' * lb
2025-07-02 05:52:10.553 elif tag == 'delete':
2025-07-02 05:52:10.560 atags += '-' * la
2025-07-02 05:52:10.567 elif tag == 'insert':
2025-07-02 05:52:10.574 btags += '+' * lb
2025-07-02 05:52:10.584 elif tag == 'equal':
2025-07-02 05:52:10.595 atags += ' ' * la
2025-07-02 05:52:10.605 btags += ' ' * lb
2025-07-02 05:52:10.611 else:
2025-07-02 05:52:10.618 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:10.624 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:10.630 else:
2025-07-02 05:52:10.637 # the synch pair is identical
2025-07-02 05:52:10.643 yield '  ' + aelt
2025-07-02 05:52:10.651
2025-07-02 05:52:10.660 # pump out diffs from after the synch point
2025-07-02 05:52:10.669 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:10.676
2025-07-02 05:52:10.682 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:10.688 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:10.694
2025-07-02 05:52:10.700 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:10.706 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:10.714 alo = 425, ahi = 1101
2025-07-02 05:52:10.721 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:10.727 blo = 425, bhi = 1101
2025-07-02 05:52:10.734
2025-07-02 05:52:10.743 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:10.751 g = []
2025-07-02 05:52:10.757 if alo < ahi:
2025-07-02 05:52:10.762 if blo < bhi:
2025-07-02 05:52:10.769 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:10.775 else:
2025-07-02 05:52:10.780 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:10.787 elif blo < bhi:
2025-07-02 05:52:10.795 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:10.803
2025-07-02 05:52:10.814 >       yield from g
2025-07-02 05:52:10.825
2025-07-02 05:52:10.835 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:10.843 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:10.851
2025-07-02 05:52:10.859 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:10.868 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:10.876 alo = 425, ahi = 1101
2025-07-02 05:52:10.883 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:10.896 blo = 425, bhi = 1101
2025-07-02 05:52:10.903
2025-07-02 05:52:10.910 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:10.916 r"""
2025-07-02 05:52:10.924 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:10.931 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:10.939 synch point, and intraline difference marking is done on the
2025-07-02 05:52:10.947 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:10.954
2025-07-02 05:52:10.960 Example:
2025-07-02 05:52:10.967
2025-07-02 05:52:10.975 >>> d = Differ()
2025-07-02 05:52:10.981 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:10.986 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:10.992 >>> print(''.join(results), end="")
2025-07-02 05:52:10.998 - abcDefghiJkl
2025-07-02 05:52:11.015 + abcdefGhijkl
2025-07-02 05:52:11.033 """
2025-07-02 05:52:11.042
2025-07-02 05:52:11.048 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:11.054 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:11.059 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:11.064 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:11.071 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:11.076
2025-07-02 05:52:11.081 # search for the pair that matches best without being identical
2025-07-02 05:52:11.087 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:11.093 # on junk -- unless we have to)
2025-07-02 05:52:11.099 for j in range(blo, bhi):
2025-07-02 05:52:11.104 bj = b[j]
2025-07-02 05:52:11.110 cruncher.set_seq2(bj)
2025-07-02 05:52:11.116 for i in range(alo, ahi):
2025-07-02 05:52:11.123 ai = a[i]
2025-07-02 05:52:11.132 if ai == bj:
2025-07-02 05:52:11.140 if eqi is None:
2025-07-02 05:52:11.146 eqi, eqj = i, j
2025-07-02 05:52:11.152 continue
2025-07-02 05:52:11.157 cruncher.set_seq1(ai)
2025-07-02 05:52:11.162 # computing similarity is expensive, so use the quick
2025-07-02 05:52:11.167 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:11.172 # compares by a factor of 3.
2025-07-02 05:52:11.177 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:11.185 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:11.198 # of the computation is cached by cruncher
2025-07-02 05:52:11.209 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:11.221 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:11.231 cruncher.ratio() > best_ratio:
2025-07-02 05:52:11.242 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:11.255 if best_ratio < cutoff:
2025-07-02 05:52:11.265 # no non-identical "pretty close" pair
2025-07-02 05:52:11.274 if eqi is None:
2025-07-02 05:52:11.282 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:11.289 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:11.296 return
2025-07-02 05:52:11.301 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:11.309 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:11.315 else:
2025-07-02 05:52:11.321 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:11.326 eqi = None
2025-07-02 05:52:11.335
2025-07-02 05:52:11.341 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:11.346 # identical
2025-07-02 05:52:11.351
2025-07-02 05:52:11.357 # pump out diffs from before the synch point
2025-07-02 05:52:11.363 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:11.369
2025-07-02 05:52:11.375 # do intraline marking on the synch pair
2025-07-02 05:52:11.385 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:11.398 if eqi is None:
2025-07-02 05:52:11.413 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:11.425 atags = btags = ""
2025-07-02 05:52:11.434 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:11.443 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:11.452 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:11.460 if tag == 'replace':
2025-07-02 05:52:11.467 atags += '^' * la
2025-07-02 05:52:11.475 btags += '^' * lb
2025-07-02 05:52:11.483 elif tag == 'delete':
2025-07-02 05:52:11.495 atags += '-' * la
2025-07-02 05:52:11.503 elif tag == 'insert':
2025-07-02 05:52:11.511 btags += '+' * lb
2025-07-02 05:52:11.528 elif tag == 'equal':
2025-07-02 05:52:11.542 atags += ' ' * la
2025-07-02 05:52:11.549 btags += ' ' * lb
2025-07-02 05:52:11.556 else:
2025-07-02 05:52:11.562 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:11.568 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:11.574 else:
2025-07-02 05:52:11.579 # the synch pair is identical
2025-07-02 05:52:11.585 yield '  ' + aelt
2025-07-02 05:52:11.591
2025-07-02 05:52:11.597 # pump out diffs from after the synch point
2025-07-02 05:52:11.603 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:11.610
2025-07-02 05:52:11.617 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:11.630 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:11.642
2025-07-02 05:52:11.653 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:11.661 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:11.668 alo = 426, ahi = 1101
2025-07-02 05:52:11.675 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:11.680 blo = 426, bhi = 1101
2025-07-02 05:52:11.692
2025-07-02 05:52:11.704 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:11.712 g = []
2025-07-02 05:52:11.720 if alo < ahi:
2025-07-02 05:52:11.727 if blo < bhi:
2025-07-02 05:52:11.739 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:11.752 else:
2025-07-02 05:52:11.763 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:11.778 elif blo < bhi:
2025-07-02 05:52:11.785 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:11.791
2025-07-02 05:52:11.798 >       yield from g
2025-07-02 05:52:11.804
2025-07-02 05:52:11.810 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:11.820 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:11.829
2025-07-02 05:52:11.836 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:11.844 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:11.851 alo = 426, ahi = 1101
2025-07-02 05:52:11.862 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:11.876 blo = 426, bhi = 1101
2025-07-02 05:52:11.886
2025-07-02 05:52:11.893 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:11.906 r"""
2025-07-02 05:52:11.916 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:11.925 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:11.933 synch point, and intraline difference marking is done on the
2025-07-02 05:52:11.940 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:11.950
2025-07-02 05:52:11.959 Example:
2025-07-02 05:52:11.967
2025-07-02 05:52:11.975 >>> d = Differ()
2025-07-02 05:52:11.984 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:11.993 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:12.006 >>> print(''.join(results), end="")
2025-07-02 05:52:12.016 - abcDefghiJkl
2025-07-02 05:52:12.034 + abcdefGhijkl
2025-07-02 05:52:12.060 """
2025-07-02 05:52:12.069
2025-07-02 05:52:12.082 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:12.091 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:12.098 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:12.108 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:12.119 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:12.127
2025-07-02 05:52:12.135 # search for the pair that matches best without being identical
2025-07-02 05:52:12.146 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:12.154 # on junk -- unless we have to)
2025-07-02 05:52:12.161 for j in range(blo, bhi):
2025-07-02 05:52:12.169 bj = b[j]
2025-07-02 05:52:12.176 cruncher.set_seq2(bj)
2025-07-02 05:52:12.183 for i in range(alo, ahi):
2025-07-02 05:52:12.193 ai = a[i]
2025-07-02 05:52:12.206 if ai == bj:
2025-07-02 05:52:12.215 if eqi is None:
2025-07-02 05:52:12.222 eqi, eqj = i, j
2025-07-02 05:52:12.230 continue
2025-07-02 05:52:12.238 cruncher.set_seq1(ai)
2025-07-02 05:52:12.245 # computing similarity is expensive, so use the quick
2025-07-02 05:52:12.253 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:12.260 # compares by a factor of 3.
2025-07-02 05:52:12.268 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:12.278 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:12.291 # of the computation is cached by cruncher
2025-07-02 05:52:12.301 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:12.314 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:12.327 cruncher.ratio() > best_ratio:
2025-07-02 05:52:12.338 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:12.347 if best_ratio < cutoff:
2025-07-02 05:52:12.355 # no non-identical "pretty close" pair
2025-07-02 05:52:12.363 if eqi is None:
2025-07-02 05:52:12.371 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:12.385 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:12.394 return
2025-07-02 05:52:12.401 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:12.410 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:12.417 else:
2025-07-02 05:52:12.422 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:12.430 eqi = None
2025-07-02 05:52:12.439
2025-07-02 05:52:12.446 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:12.453 # identical
2025-07-02 05:52:12.459
2025-07-02 05:52:12.465 # pump out diffs from before the synch point
2025-07-02 05:52:12.471 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:12.477
2025-07-02 05:52:12.483 # do intraline marking on the synch pair
2025-07-02 05:52:12.496 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:12.508 if eqi is None:
2025-07-02 05:52:12.518 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:12.526 atags = btags = ""
2025-07-02 05:52:12.535 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:12.542 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:12.553 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:12.565 if tag == 'replace':
2025-07-02 05:52:12.574 atags += '^' * la
2025-07-02 05:52:12.583 btags += '^' * lb
2025-07-02 05:52:12.591 elif tag == 'delete':
2025-07-02 05:52:12.603 atags += '-' * la
2025-07-02 05:52:12.611 elif tag == 'insert':
2025-07-02 05:52:12.618 btags += '+' * lb
2025-07-02 05:52:12.627 elif tag == 'equal':
2025-07-02 05:52:12.639 atags += ' ' * la
2025-07-02 05:52:12.648 btags += ' ' * lb
2025-07-02 05:52:12.656 else:
2025-07-02 05:52:12.662 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:12.668 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:12.674 else:
2025-07-02 05:52:12.682 # the synch pair is identical
2025-07-02 05:52:12.693 yield '  ' + aelt
2025-07-02 05:52:12.700
2025-07-02 05:52:12.706 # pump out diffs from after the synch point
2025-07-02 05:52:12.711 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:12.722
2025-07-02 05:52:12.731 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:12.738 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:12.745
2025-07-02 05:52:12.752 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:12.760 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:12.766 alo = 427, ahi = 1101
2025-07-02 05:52:12.772 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:12.780 blo = 427, bhi = 1101
2025-07-02 05:52:12.787
2025-07-02 05:52:12.795 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:12.802 g = []
2025-07-02 05:52:12.814 if alo < ahi:
2025-07-02 05:52:12.824 if blo < bhi:
2025-07-02 05:52:12.833 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:12.839 else:
2025-07-02 05:52:12.845 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:12.854 elif blo < bhi:
2025-07-02 05:52:12.865 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:12.878
2025-07-02 05:52:12.888 >       yield from g
2025-07-02 05:52:12.897
2025-07-02 05:52:12.904 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:12.910 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:12.916
2025-07-02 05:52:12.924 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:12.937 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:12.945 alo = 427, ahi = 1101
2025-07-02 05:52:12.952 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:12.958 blo = 427, bhi = 1101
2025-07-02 05:52:12.967
2025-07-02 05:52:12.980 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:12.993 r"""
2025-07-02 05:52:13.003 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:13.012 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:13.020 synch point, and intraline difference marking is done on the
2025-07-02 05:52:13.028 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:13.034
2025-07-02 05:52:13.043 Example:
2025-07-02 05:52:13.051
2025-07-02 05:52:13.059 >>> d = Differ()
2025-07-02 05:52:13.067 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:13.079 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:13.087 >>> print(''.join(results), end="")
2025-07-02 05:52:13.095 - abcDefghiJkl
2025-07-02 05:52:13.108 + abcdefGhijkl
2025-07-02 05:52:13.128 """
2025-07-02 05:52:13.138
2025-07-02 05:52:13.147 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:13.156 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:13.162 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:13.174 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:13.186 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:13.197
2025-07-02 05:52:13.208 # search for the pair that matches best without being identical
2025-07-02 05:52:13.219 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:13.228 # on junk -- unless we have to)
2025-07-02 05:52:13.238 for j in range(blo, bhi):
2025-07-02 05:52:13.247 bj = b[j]
2025-07-02 05:52:13.256 cruncher.set_seq2(bj)
2025-07-02 05:52:13.263 for i in range(alo, ahi):
2025-07-02 05:52:13.268 ai = a[i]
2025-07-02 05:52:13.273 if ai == bj:
2025-07-02 05:52:13.278 if eqi is None:
2025-07-02 05:52:13.282 eqi, eqj = i, j
2025-07-02 05:52:13.287 continue
2025-07-02 05:52:13.292 cruncher.set_seq1(ai)
2025-07-02 05:52:13.297 # computing similarity is expensive, so use the quick
2025-07-02 05:52:13.301 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:13.307 # compares by a factor of 3.
2025-07-02 05:52:13.318 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:13.331 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:13.342 # of the computation is cached by cruncher
2025-07-02 05:52:13.355 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:13.362 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:13.371 cruncher.ratio() > best_ratio:
2025-07-02 05:52:13.378 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:13.383 if best_ratio < cutoff:
2025-07-02 05:52:13.388 # no non-identical "pretty close" pair
2025-07-02 05:52:13.394 if eqi is None:
2025-07-02 05:52:13.399 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:13.404 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:13.408 return
2025-07-02 05:52:13.413 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:13.418 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:13.422 else:
2025-07-02 05:52:13.427 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:13.433 eqi = None
2025-07-02 05:52:13.437
2025-07-02 05:52:13.442 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:13.447 # identical
2025-07-02 05:52:13.452
2025-07-02 05:52:13.457 # pump out diffs from before the synch point
2025-07-02 05:52:13.463 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:13.470
2025-07-02 05:52:13.481 # do intraline marking on the synch pair
2025-07-02 05:52:13.493 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:13.507 if eqi is None:
2025-07-02 05:52:13.518 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:13.526 atags = btags = ""
2025-07-02 05:52:13.534 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:13.541 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:13.549 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:13.556 if tag == 'replace':
2025-07-02 05:52:13.564 atags += '^' * la
2025-07-02 05:52:13.571 btags += '^' * lb
2025-07-02 05:52:13.578 elif tag == 'delete':
2025-07-02 05:52:13.586 atags += '-' * la
2025-07-02 05:52:13.599 elif tag == 'insert':
2025-07-02 05:52:13.606 btags += '+' * lb
2025-07-02 05:52:13.613 elif tag == 'equal':
2025-07-02 05:52:13.619 atags += ' ' * la
2025-07-02 05:52:13.627 btags += ' ' * lb
2025-07-02 05:52:13.634 else:
2025-07-02 05:52:13.647 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:13.658 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:13.665 else:
2025-07-02 05:52:13.671 # the synch pair is identical
2025-07-02 05:52:13.678 yield '  ' + aelt
2025-07-02 05:52:13.689
2025-07-02 05:52:13.697 # pump out diffs from after the synch point
2025-07-02 05:52:13.703 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:13.711
2025-07-02 05:52:13.722 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:13.731 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:13.738
2025-07-02 05:52:13.746 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:13.755 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:13.761 alo = 428, ahi = 1101
2025-07-02 05:52:13.769 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:13.774 blo = 428, bhi = 1101
2025-07-02 05:52:13.779
2025-07-02 05:52:13.783 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:13.788 g = []
2025-07-02 05:52:13.793 if alo < ahi:
2025-07-02 05:52:13.797 if blo < bhi:
2025-07-02 05:52:13.802 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:13.806 else:
2025-07-02 05:52:13.812 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:13.824 elif blo < bhi:
2025-07-02 05:52:13.837 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:13.849
2025-07-02 05:52:13.858 >       yield from g
2025-07-02 05:52:13.871
2025-07-02 05:52:13.882 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:13.893 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:13.900
2025-07-02 05:52:13.906 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:13.915 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:13.926 alo = 428, ahi = 1101
2025-07-02 05:52:13.935 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:13.941 blo = 428, bhi = 1101
2025-07-02 05:52:13.953
2025-07-02 05:52:13.963 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:13.971 r"""
2025-07-02 05:52:13.979 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:13.991 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:14.001 synch point, and intraline difference marking is done on the
2025-07-02 05:52:14.013 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:14.025
2025-07-02 05:52:14.037 Example:
2025-07-02 05:52:14.045
2025-07-02 05:52:14.053 >>> d = Differ()
2025-07-02 05:52:14.059 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:14.065 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:14.071 >>> print(''.join(results), end="")
2025-07-02 05:52:14.079 - abcDefghiJkl
2025-07-02 05:52:14.102 + abcdefGhijkl
2025-07-02 05:52:14.124 """
2025-07-02 05:52:14.135
2025-07-02 05:52:14.147 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:14.157 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:14.166 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:14.174 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:14.181 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:14.187
2025-07-02 05:52:14.201 # search for the pair that matches best without being identical
2025-07-02 05:52:14.212 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:14.223 # on junk -- unless we have to)
2025-07-02 05:52:14.230 for j in range(blo, bhi):
2025-07-02 05:52:14.237 bj = b[j]
2025-07-02 05:52:14.244 cruncher.set_seq2(bj)
2025-07-02 05:52:14.256 for i in range(alo, ahi):
2025-07-02 05:52:14.272 ai = a[i]
2025-07-02 05:52:14.284 if ai == bj:
2025-07-02 05:52:14.298 if eqi is None:
2025-07-02 05:52:14.313 eqi, eqj = i, j
2025-07-02 05:52:14.326 continue
2025-07-02 05:52:14.337 cruncher.set_seq1(ai)
2025-07-02 05:52:14.349 # computing similarity is expensive, so use the quick
2025-07-02 05:52:14.360 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:14.374 # compares by a factor of 3.
2025-07-02 05:52:14.392 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:14.401 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:14.408 # of the computation is cached by cruncher
2025-07-02 05:52:14.415 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:14.420 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:14.425 cruncher.ratio() > best_ratio:
2025-07-02 05:52:14.430 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:14.435 if best_ratio < cutoff:
2025-07-02 05:52:14.441 # no non-identical "pretty close" pair
2025-07-02 05:52:14.446 if eqi is None:
2025-07-02 05:52:14.453 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:14.459 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:14.464 return
2025-07-02 05:52:14.471 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:14.479 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:14.491 else:
2025-07-02 05:52:14.506 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:14.517 eqi = None
2025-07-02 05:52:14.527
2025-07-02 05:52:14.535 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:14.541 # identical
2025-07-02 05:52:14.546
2025-07-02 05:52:14.551 # pump out diffs from before the synch point
2025-07-02 05:52:14.556 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:14.562
2025-07-02 05:52:14.572 # do intraline marking on the synch pair
2025-07-02 05:52:14.583 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:14.591 if eqi is None:
2025-07-02 05:52:14.597 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:14.610 atags = btags = ""
2025-07-02 05:52:14.619 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:14.628 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:14.635 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:14.643 if tag == 'replace':
2025-07-02 05:52:14.652 atags += '^' * la
2025-07-02 05:52:14.663 btags += '^' * lb
2025-07-02 05:52:14.675 elif tag == 'delete':
2025-07-02 05:52:14.686 atags += '-' * la
2025-07-02 05:52:14.697 elif tag == 'insert':
2025-07-02 05:52:14.707 btags += '+' * lb
2025-07-02 05:52:14.717 elif tag == 'equal':
2025-07-02 05:52:14.729 atags += ' ' * la
2025-07-02 05:52:14.739 btags += ' ' * lb
2025-07-02 05:52:14.747 else:
2025-07-02 05:52:14.755 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:14.762 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:14.768 else:
2025-07-02 05:52:14.774 # the synch pair is identical
2025-07-02 05:52:14.780 yield '  ' + aelt
2025-07-02 05:52:14.786
2025-07-02 05:52:14.796 # pump out diffs from after the synch point
2025-07-02 05:52:14.805 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:14.811
2025-07-02 05:52:14.817 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:14.827 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:14.837
2025-07-02 05:52:14.846 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:14.855 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:14.862 alo = 429, ahi = 1101
2025-07-02 05:52:14.873 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:14.885 blo = 429, bhi = 1101
2025-07-02 05:52:14.895
2025-07-02 05:52:14.906 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:14.917 g = []
2025-07-02 05:52:14.928 if alo < ahi:
2025-07-02 05:52:14.938 if blo < bhi:
2025-07-02 05:52:14.952 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:14.964 else:
2025-07-02 05:52:14.973 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:14.981 elif blo < bhi:
2025-07-02 05:52:14.993 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:15.005
2025-07-02 05:52:15.013 >       yield from g
2025-07-02 05:52:15.029
2025-07-02 05:52:15.042 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:15.055 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:15.067
2025-07-02 05:52:15.073 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:15.081 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:15.087 alo = 429, ahi = 1101
2025-07-02 05:52:15.095 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:15.103 blo = 429, bhi = 1101
2025-07-02 05:52:15.114
2025-07-02 05:52:15.123 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:15.129 r"""
2025-07-02 05:52:15.137 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:15.150 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:15.160 synch point, and intraline difference marking is done on the
2025-07-02 05:52:15.168 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:15.175
2025-07-02 05:52:15.191 Example:
2025-07-02 05:52:15.200
2025-07-02 05:52:15.208 >>> d = Differ()
2025-07-02 05:52:15.215 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:15.221 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:15.240 >>> print(''.join(results), end="")
2025-07-02 05:52:15.250 - abcDefghiJkl
2025-07-02 05:52:15.269 + abcdefGhijkl
2025-07-02 05:52:15.287 """
2025-07-02 05:52:15.293
2025-07-02 05:52:15.299 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:15.306 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:15.312 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:15.318 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:15.325 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:15.331
2025-07-02 05:52:15.346 # search for the pair that matches best without being identical
2025-07-02 05:52:15.357 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:15.371 # on junk -- unless we have to)
2025-07-02 05:52:15.381 for j in range(blo, bhi):
2025-07-02 05:52:15.393 bj = b[j]
2025-07-02 05:52:15.404 cruncher.set_seq2(bj)
2025-07-02 05:52:15.417 for i in range(alo, ahi):
2025-07-02 05:52:15.425 ai = a[i]
2025-07-02 05:52:15.439 if ai == bj:
2025-07-02 05:52:15.449 if eqi is None:
2025-07-02 05:52:15.457 eqi, eqj = i, j
2025-07-02 05:52:15.463 continue
2025-07-02 05:52:15.468 cruncher.set_seq1(ai)
2025-07-02 05:52:15.475 # computing similarity is expensive, so use the quick
2025-07-02 05:52:15.481 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:15.493 # compares by a factor of 3.
2025-07-02 05:52:15.505 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:15.514 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:15.524 # of the computation is cached by cruncher
2025-07-02 05:52:15.535 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:15.546 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:15.555 cruncher.ratio() > best_ratio:
2025-07-02 05:52:15.562 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:15.567 if best_ratio < cutoff:
2025-07-02 05:52:15.573 # no non-identical "pretty close" pair
2025-07-02 05:52:15.578 if eqi is None:
2025-07-02 05:52:15.583 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:15.590 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:15.596 return
2025-07-02 05:52:15.603 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:15.609 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:15.615 else:
2025-07-02 05:52:15.625 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:15.633 eqi = None
2025-07-02 05:52:15.640
2025-07-02 05:52:15.647 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:15.653 # identical
2025-07-02 05:52:15.659
2025-07-02 05:52:15.669 # pump out diffs from before the synch point
2025-07-02 05:52:15.681 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:15.691
2025-07-02 05:52:15.703 # do intraline marking on the synch pair
2025-07-02 05:52:15.716 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:15.726 if eqi is None:
2025-07-02 05:52:15.734 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:15.742 atags = btags = ""
2025-07-02 05:52:15.749 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:15.755 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:15.761 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:15.771 if tag == 'replace':
2025-07-02 05:52:15.782 atags += '^' * la
2025-07-02 05:52:15.791 btags += '^' * lb
2025-07-02 05:52:15.801 elif tag == 'delete':
2025-07-02 05:52:15.812 atags += '-' * la
2025-07-02 05:52:15.825 elif tag == 'insert':
2025-07-02 05:52:15.834 btags += '+' * lb
2025-07-02 05:52:15.842 elif tag == 'equal':
2025-07-02 05:52:15.851 atags += ' ' * la
2025-07-02 05:52:15.857 btags += ' ' * lb
2025-07-02 05:52:15.866 else:
2025-07-02 05:52:15.876 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:15.885 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:15.892 else:
2025-07-02 05:52:15.899 # the synch pair is identical
2025-07-02 05:52:15.905 yield '  ' + aelt
2025-07-02 05:52:15.911
2025-07-02 05:52:15.918 # pump out diffs from after the synch point
2025-07-02 05:52:15.924 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:15.931
2025-07-02 05:52:15.937 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:15.943 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:15.952
2025-07-02 05:52:15.966 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:15.976 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:15.984 alo = 430, ahi = 1101
2025-07-02 05:52:15.999 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:16.012 blo = 430, bhi = 1101
2025-07-02 05:52:16.022
2025-07-02 05:52:16.031 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:16.039 g = []
2025-07-02 05:52:16.045 if alo < ahi:
2025-07-02 05:52:16.052 if blo < bhi:
2025-07-02 05:52:16.058 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:16.066 else:
2025-07-02 05:52:16.074 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:16.086 elif blo < bhi:
2025-07-02 05:52:16.096 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:16.103
2025-07-02 05:52:16.109 >       yield from g
2025-07-02 05:52:16.115
2025-07-02 05:52:16.121 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:16.126 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:16.131
2025-07-02 05:52:16.136 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:16.147 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:16.157 alo = 430, ahi = 1101
2025-07-02 05:52:16.166 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:16.175 blo = 430, bhi = 1101
2025-07-02 05:52:16.189
2025-07-02 05:52:16.199 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:16.207 r"""
2025-07-02 05:52:16.214 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:16.221 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:16.229 synch point, and intraline difference marking is done on the
2025-07-02 05:52:16.237 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:16.244
2025-07-02 05:52:16.256 Example:
2025-07-02 05:52:16.269
2025-07-02 05:52:16.282 >>> d = Differ()
2025-07-02 05:52:16.292 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:16.302 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:16.312 >>> print(''.join(results), end="")
2025-07-02 05:52:16.325 - abcDefghiJkl
2025-07-02 05:52:16.342 + abcdefGhijkl
2025-07-02 05:52:16.360 """
2025-07-02 05:52:16.366
2025-07-02 05:52:16.372 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:16.378 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:16.383 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:16.390 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:16.399 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:16.413
2025-07-02 05:52:16.423 # search for the pair that matches best without being identical
2025-07-02 05:52:16.433 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:16.444 # on junk -- unless we have to)
2025-07-02 05:52:16.453 for j in range(blo, bhi):
2025-07-02 05:52:16.459 bj = b[j]
2025-07-02 05:52:16.466 cruncher.set_seq2(bj)
2025-07-02 05:52:16.475 for i in range(alo, ahi):
2025-07-02 05:52:16.482 ai = a[i]
2025-07-02 05:52:16.492 if ai == bj:
2025-07-02 05:52:16.505 if eqi is None:
2025-07-02 05:52:16.515 eqi, eqj = i, j
2025-07-02 05:52:16.524 continue
2025-07-02 05:52:16.537 cruncher.set_seq1(ai)
2025-07-02 05:52:16.546 # computing similarity is expensive, so use the quick
2025-07-02 05:52:16.555 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:16.564 # compares by a factor of 3.
2025-07-02 05:52:16.571 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:16.579 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:16.588 # of the computation is cached by cruncher
2025-07-02 05:52:16.600 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:16.611 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:16.617 cruncher.ratio() > best_ratio:
2025-07-02 05:52:16.624 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:16.631 if best_ratio < cutoff:
2025-07-02 05:52:16.641 # no non-identical "pretty close" pair
2025-07-02 05:52:16.650 if eqi is None:
2025-07-02 05:52:16.659 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:16.668 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:16.676 return
2025-07-02 05:52:16.683 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:16.694 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:16.706 else:
2025-07-02 05:52:16.717 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:16.725 eqi = None
2025-07-02 05:52:16.734
2025-07-02 05:52:16.744 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:16.751 # identical
2025-07-02 05:52:16.758
2025-07-02 05:52:16.769 # pump out diffs from before the synch point
2025-07-02 05:52:16.779 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:16.787
2025-07-02 05:52:16.794 # do intraline marking on the synch pair
2025-07-02 05:52:16.801 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:16.813 if eqi is None:
2025-07-02 05:52:16.824 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:16.834 atags = btags = ""
2025-07-02 05:52:16.848 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:16.857 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:16.864 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:16.876 if tag == 'replace':
2025-07-02 05:52:16.887 atags += '^' * la
2025-07-02 05:52:16.899 btags += '^' * lb
2025-07-02 05:52:16.909 elif tag == 'delete':
2025-07-02 05:52:16.922 atags += '-' * la
2025-07-02 05:52:16.934 elif tag == 'insert':
2025-07-02 05:52:16.947 btags += '+' * lb
2025-07-02 05:52:16.961 elif tag == 'equal':
2025-07-02 05:52:16.974 atags += ' ' * la
2025-07-02 05:52:16.987 btags += ' ' * lb
2025-07-02 05:52:16.997 else:
2025-07-02 05:52:17.005 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:17.012 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:17.023 else:
2025-07-02 05:52:17.031 # the synch pair is identical
2025-07-02 05:52:17.039 yield '  ' + aelt
2025-07-02 05:52:17.050
2025-07-02 05:52:17.060 # pump out diffs from after the synch point
2025-07-02 05:52:17.071 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:17.080
2025-07-02 05:52:17.092 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:17.102 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:17.109
2025-07-02 05:52:17.115 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:17.122 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:17.130 alo = 431, ahi = 1101
2025-07-02 05:52:17.142 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:17.151 blo = 431, bhi = 1101
2025-07-02 05:52:17.157
2025-07-02 05:52:17.164 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:17.172 g = []
2025-07-02 05:52:17.179 if alo < ahi:
2025-07-02 05:52:17.186 if blo < bhi:
2025-07-02 05:52:17.197 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:17.208 else:
2025-07-02 05:52:17.215 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:17.225 elif blo < bhi:
2025-07-02 05:52:17.236 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:17.244
2025-07-02 05:52:17.252 >       yield from g
2025-07-02 05:52:17.259
2025-07-02 05:52:17.265 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:17.272 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:17.279
2025-07-02 05:52:17.287 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:17.296 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:17.303 alo = 431, ahi = 1101
2025-07-02 05:52:17.310 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:17.317 blo = 431, bhi = 1101
2025-07-02 05:52:17.328
2025-07-02 05:52:17.339 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:17.346 r"""
2025-07-02 05:52:17.355 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:17.362 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:17.369 synch point, and intraline difference marking is done on the
2025-07-02 05:52:17.375 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:17.382
2025-07-02 05:52:17.388 Example:
2025-07-02 05:52:17.395
2025-07-02 05:52:17.407 >>> d = Differ()
2025-07-02 05:52:17.415 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:17.421 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:17.426 >>> print(''.join(results), end="")
2025-07-02 05:52:17.431 - abcDefghiJkl
2025-07-02 05:52:17.444 + abcdefGhijkl
2025-07-02 05:52:17.458 """
2025-07-02 05:52:17.467
2025-07-02 05:52:17.474 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:17.485 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:17.495 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:17.507 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:17.517 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:17.526
2025-07-02 05:52:17.534 # search for the pair that matches best without being identical
2025-07-02 05:52:17.543 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:17.548 # on junk -- unless we have to)
2025-07-02 05:52:17.555 for j in range(blo, bhi):
2025-07-02 05:52:17.562 bj = b[j]
2025-07-02 05:52:17.570 cruncher.set_seq2(bj)
2025-07-02 05:52:17.582 for i in range(alo, ahi):
2025-07-02 05:52:17.592 ai = a[i]
2025-07-02 05:52:17.598 if ai == bj:
2025-07-02 05:52:17.604 if eqi is None:
2025-07-02 05:52:17.609 eqi, eqj = i, j
2025-07-02 05:52:17.614 continue
2025-07-02 05:52:17.620 cruncher.set_seq1(ai)
2025-07-02 05:52:17.632 # computing similarity is expensive, so use the quick
2025-07-02 05:52:17.641 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:17.648 # compares by a factor of 3.
2025-07-02 05:52:17.656 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:17.663 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:17.671 # of the computation is cached by cruncher
2025-07-02 05:52:17.680 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:17.688 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:17.694 cruncher.ratio() > best_ratio:
2025-07-02 05:52:17.700 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:17.710 if best_ratio < cutoff:
2025-07-02 05:52:17.719 # no non-identical "pretty close" pair
2025-07-02 05:52:17.726 if eqi is None:
2025-07-02 05:52:17.731 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:17.737 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:17.744 return
2025-07-02 05:52:17.755 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:17.762 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:17.768 else:
2025-07-02 05:52:17.775 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:17.782 eqi = None
2025-07-02 05:52:17.791
2025-07-02 05:52:17.801 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:17.814 # identical
2025-07-02 05:52:17.823
2025-07-02 05:52:17.835 # pump out diffs from before the synch point
2025-07-02 05:52:17.846 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:17.857
2025-07-02 05:52:17.869 # do intraline marking on the synch pair
2025-07-02 05:52:17.881 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:17.893 if eqi is None:
2025-07-02 05:52:17.904 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:17.913 atags = btags = ""
2025-07-02 05:52:17.920 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:17.926 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:17.932 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:17.938 if tag == 'replace':
2025-07-02 05:52:17.945 atags += '^' * la
2025-07-02 05:52:17.952 btags += '^' * lb
2025-07-02 05:52:17.958 elif tag == 'delete':
2025-07-02 05:52:17.964 atags += '-' * la
2025-07-02 05:52:17.971 elif tag == 'insert':
2025-07-02 05:52:17.979 btags += '+' * lb
2025-07-02 05:52:17.993 elif tag == 'equal':
2025-07-02 05:52:18.003 atags += ' ' * la
2025-07-02 05:52:18.016 btags += ' ' * lb
2025-07-02 05:52:18.027 else:
2025-07-02 05:52:18.041 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:18.054 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:18.067 else:
2025-07-02 05:52:18.082 # the synch pair is identical
2025-07-02 05:52:18.094 yield '  ' + aelt
2025-07-02 05:52:18.107
2025-07-02 05:52:18.118 # pump out diffs from after the synch point
2025-07-02 05:52:18.126 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:18.134
2025-07-02 05:52:18.143 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:18.149 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:18.155
2025-07-02 05:52:18.161 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:18.169 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:18.176 alo = 432, ahi = 1101
2025-07-02 05:52:18.183 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:18.191 blo = 432, bhi = 1101
2025-07-02 05:52:18.201
2025-07-02 05:52:18.210 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:18.221 g = []
2025-07-02 05:52:18.234 if alo < ahi:
2025-07-02 05:52:18.243 if blo < bhi:
2025-07-02 05:52:18.250 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:18.256 else:
2025-07-02 05:52:18.261 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:18.265 elif blo < bhi:
2025-07-02 05:52:18.271 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:18.278
2025-07-02 05:52:18.289 >       yield from g
2025-07-02 05:52:18.299
2025-07-02 05:52:18.309 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:18.319 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:18.327
2025-07-02 05:52:18.333 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:18.342 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:18.348 alo = 432, ahi = 1101
2025-07-02 05:52:18.357 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:18.365 blo = 432, bhi = 1101
2025-07-02 05:52:18.372
2025-07-02 05:52:18.381 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:18.389 r"""
2025-07-02 05:52:18.397 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:18.409 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:18.419 synch point, and intraline difference marking is done on the
2025-07-02 05:52:18.428 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:18.435
2025-07-02 05:52:18.442 Example:
2025-07-02 05:52:18.449
2025-07-02 05:52:18.456 >>> d = Differ()
2025-07-02 05:52:18.463 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:18.470 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:18.477 >>> print(''.join(results), end="")
2025-07-02 05:52:18.483 - abcDefghiJkl
2025-07-02 05:52:18.495 + abcdefGhijkl
2025-07-02 05:52:18.507 """
2025-07-02 05:52:18.512
2025-07-02 05:52:18.518 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:18.530 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:18.543 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:18.552 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:18.561 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:18.569
2025-07-02 05:52:18.577 # search for the pair that matches best without being identical
2025-07-02 05:52:18.585 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:18.598 # on junk -- unless we have to)
2025-07-02 05:52:18.611 for j in range(blo, bhi):
2025-07-02 05:52:18.620 bj = b[j]
2025-07-02 05:52:18.627 cruncher.set_seq2(bj)
2025-07-02 05:52:18.634 for i in range(alo, ahi):
2025-07-02 05:52:18.646 ai = a[i]
2025-07-02 05:52:18.657 if ai == bj:
2025-07-02 05:52:18.665 if eqi is None:
2025-07-02 05:52:18.673 eqi, eqj = i, j
2025-07-02 05:52:18.681 continue
2025-07-02 05:52:18.688 cruncher.set_seq1(ai)
2025-07-02 05:52:18.696 # computing similarity is expensive, so use the quick
2025-07-02 05:52:18.704 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:18.712 # compares by a factor of 3.
2025-07-02 05:52:18.720 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:18.728 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:18.736 # of the computation is cached by cruncher
2025-07-02 05:52:18.744 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:18.752 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:18.759 cruncher.ratio() > best_ratio:
2025-07-02 05:52:18.766 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:18.773 if best_ratio < cutoff:
2025-07-02 05:52:18.779 # no non-identical "pretty close" pair
2025-07-02 05:52:18.785 if eqi is None:
2025-07-02 05:52:18.792 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:18.799 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:18.807 return
2025-07-02 05:52:18.819 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:18.829 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:18.836 else:
2025-07-02 05:52:18.846 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:18.856 eqi = None
2025-07-02 05:52:18.864
2025-07-02 05:52:18.871 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:18.878 # identical
2025-07-02 05:52:18.885
2025-07-02 05:52:18.892 # pump out diffs from before the synch point
2025-07-02 05:52:18.898 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:18.908
2025-07-02 05:52:18.919 # do intraline marking on the synch pair
2025-07-02 05:52:18.928 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:18.935 if eqi is None:
2025-07-02 05:52:18.943 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:18.954 atags = btags = ""
2025-07-02 05:52:18.965 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:18.973 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:18.980 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:18.987 if tag == 'replace':
2025-07-02 05:52:18.994 atags += '^' * la
2025-07-02 05:52:19.001 btags += '^' * lb
2025-07-02 05:52:19.013 elif tag == 'delete':
2025-07-02 05:52:19.024 atags += '-' * la
2025-07-02 05:52:19.033 elif tag == 'insert':
2025-07-02 05:52:19.040 btags += '+' * lb
2025-07-02 05:52:19.047 elif tag == 'equal':
2025-07-02 05:52:19.055 atags += ' ' * la
2025-07-02 05:52:19.068 btags += ' ' * lb
2025-07-02 05:52:19.076 else:
2025-07-02 05:52:19.083 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:19.090 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:19.101 else:
2025-07-02 05:52:19.112 # the synch pair is identical
2025-07-02 05:52:19.126 yield '  ' + aelt
2025-07-02 05:52:19.136
2025-07-02 05:52:19.145 # pump out diffs from after the synch point
2025-07-02 05:52:19.152 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:19.159
2025-07-02 05:52:19.165 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:19.176 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:19.185
2025-07-02 05:52:19.193 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:19.205 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:19.213 alo = 433, ahi = 1101
2025-07-02 05:52:19.220 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:19.225 blo = 433, bhi = 1101
2025-07-02 05:52:19.230
2025-07-02 05:52:19.235 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:19.243 g = []
2025-07-02 05:52:19.253 if alo < ahi:
2025-07-02 05:52:19.262 if blo < bhi:
2025-07-02 05:52:19.270 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:19.280 else:
2025-07-02 05:52:19.290 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:19.303 elif blo < bhi:
2025-07-02 05:52:19.318 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:19.330
2025-07-02 05:52:19.343 >       yield from g
2025-07-02 05:52:19.356
2025-07-02 05:52:19.368 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:19.377 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:19.384
2025-07-02 05:52:19.392 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:19.399 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:19.405 alo = 433, ahi = 1101
2025-07-02 05:52:19.414 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:19.421 blo = 433, bhi = 1101
2025-07-02 05:52:19.429
2025-07-02 05:52:19.437 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:19.444 r"""
2025-07-02 05:52:19.451 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:19.456 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:19.461 synch point, and intraline difference marking is done on the
2025-07-02 05:52:19.467 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:19.475
2025-07-02 05:52:19.482 Example:
2025-07-02 05:52:19.489
2025-07-02 05:52:19.495 >>> d = Differ()
2025-07-02 05:52:19.503 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:19.513 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:19.521 >>> print(''.join(results), end="")
2025-07-02 05:52:19.528 - abcDefghiJkl
2025-07-02 05:52:19.550 + abcdefGhijkl
2025-07-02 05:52:19.567 """
2025-07-02 05:52:19.577
2025-07-02 05:52:19.591 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:19.603 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:19.618 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:19.628 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:19.637 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:19.644
2025-07-02 05:52:19.649 # search for the pair that matches best without being identical
2025-07-02 05:52:19.655 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:19.664 # on junk -- unless we have to)
2025-07-02 05:52:19.672 for j in range(blo, bhi):
2025-07-02 05:52:19.677 bj = b[j]
2025-07-02 05:52:19.683 cruncher.set_seq2(bj)
2025-07-02 05:52:19.691 for i in range(alo, ahi):
2025-07-02 05:52:19.699 ai = a[i]
2025-07-02 05:52:19.707 if ai == bj:
2025-07-02 05:52:19.713 if eqi is None:
2025-07-02 05:52:19.724 eqi, eqj = i, j
2025-07-02 05:52:19.734 continue
2025-07-02 05:52:19.741 cruncher.set_seq1(ai)
2025-07-02 05:52:19.747 # computing similarity is expensive, so use the quick
2025-07-02 05:52:19.752 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:19.757 # compares by a factor of 3.
2025-07-02 05:52:19.762 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:19.767 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:19.772 # of the computation is cached by cruncher
2025-07-02 05:52:19.786 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:19.794 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:19.806 cruncher.ratio() > best_ratio:
2025-07-02 05:52:19.816 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:19.825 if best_ratio < cutoff:
2025-07-02 05:52:19.838 # no non-identical "pretty close" pair
2025-07-02 05:52:19.849 if eqi is None:
2025-07-02 05:52:19.862 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:19.875 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:19.890 return
2025-07-02 05:52:19.902 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:19.914 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:19.923 else:
2025-07-02 05:52:19.931 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:19.939 eqi = None
2025-07-02 05:52:19.952
2025-07-02 05:52:19.960 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:19.967 # identical
2025-07-02 05:52:19.976
2025-07-02 05:52:19.983 # pump out diffs from before the synch point
2025-07-02 05:52:19.998 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:20.006
2025-07-02 05:52:20.014 # do intraline marking on the synch pair
2025-07-02 05:52:20.021 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:20.028 if eqi is None:
2025-07-02 05:52:20.037 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:20.045 atags = btags = ""
2025-07-02 05:52:20.052 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:20.059 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:20.068 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:20.075 if tag == 'replace':
2025-07-02 05:52:20.082 atags += '^' * la
2025-07-02 05:52:20.088 btags += '^' * lb
2025-07-02 05:52:20.095 elif tag == 'delete':
2025-07-02 05:52:20.101 atags += '-' * la
2025-07-02 05:52:20.108 elif tag == 'insert':
2025-07-02 05:52:20.115 btags += '+' * lb
2025-07-02 05:52:20.121 elif tag == 'equal':
2025-07-02 05:52:20.128 atags += ' ' * la
2025-07-02 05:52:20.135 btags += ' ' * lb
2025-07-02 05:52:20.143 else:
2025-07-02 05:52:20.156 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:20.165 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:20.173 else:
2025-07-02 05:52:20.179 # the synch pair is identical
2025-07-02 05:52:20.185 yield '  ' + aelt
2025-07-02 05:52:20.192
2025-07-02 05:52:20.198 # pump out diffs from after the synch point
2025-07-02 05:52:20.209 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:20.216
2025-07-02 05:52:20.224 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:20.236 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:20.250
2025-07-02 05:52:20.258 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:20.266 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:20.273 alo = 434, ahi = 1101
2025-07-02 05:52:20.280 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:20.288 blo = 434, bhi = 1101
2025-07-02 05:52:20.295
2025-07-02 05:52:20.303 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:20.309 g = []
2025-07-02 05:52:20.315 if alo < ahi:
2025-07-02 05:52:20.322 if blo < bhi:
2025-07-02 05:52:20.329 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:20.335 else:
2025-07-02 05:52:20.343 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:20.354 elif blo < bhi:
2025-07-02 05:52:20.362 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:20.369
2025-07-02 05:52:20.375 >       yield from g
2025-07-02 05:52:20.381
2025-07-02 05:52:20.391 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:20.398 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:20.409
2025-07-02 05:52:20.419 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:20.427 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:20.433 alo = 434, ahi = 1101
2025-07-02 05:52:20.439 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:20.447 blo = 434, bhi = 1101
2025-07-02 05:52:20.454
2025-07-02 05:52:20.461 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:20.467 r"""
2025-07-02 05:52:20.473 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:20.481 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:20.487 synch point, and intraline difference marking is done on the
2025-07-02 05:52:20.494 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:20.500
2025-07-02 05:52:20.514 Example:
2025-07-02 05:52:20.523
2025-07-02 05:52:20.532 >>> d = Differ()
2025-07-02 05:52:20.545 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:20.556 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:20.568 >>> print(''.join(results), end="")
2025-07-02 05:52:20.577 - abcDefghiJkl
2025-07-02 05:52:20.597 + abcdefGhijkl
2025-07-02 05:52:20.612 """
2025-07-02 05:52:20.621
2025-07-02 05:52:20.631 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:20.638 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:20.646 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:20.651 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:20.658 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:20.665
2025-07-02 05:52:20.674 # search for the pair that matches best without being identical
2025-07-02 05:52:20.680 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:20.685 # on junk -- unless we have to)
2025-07-02 05:52:20.690 for j in range(blo, bhi):
2025-07-02 05:52:20.696 bj = b[j]
2025-07-02 05:52:20.702 cruncher.set_seq2(bj)
2025-07-02 05:52:20.711 for i in range(alo, ahi):
2025-07-02 05:52:20.717 ai = a[i]
2025-07-02 05:52:20.724 if ai == bj:
2025-07-02 05:52:20.734 if eqi is None:
2025-07-02 05:52:20.747 eqi, eqj = i, j
2025-07-02 05:52:20.755 continue
2025-07-02 05:52:20.769 cruncher.set_seq1(ai)
2025-07-02 05:52:20.780 # computing similarity is expensive, so use the quick
2025-07-02 05:52:20.795 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:20.805 # compares by a factor of 3.
2025-07-02 05:52:20.813 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:20.820 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:20.833 # of the computation is cached by cruncher
2025-07-02 05:52:20.845 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:20.852 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:20.862 cruncher.ratio() > best_ratio:
2025-07-02 05:52:20.870 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:20.878 if best_ratio < cutoff:
2025-07-02 05:52:20.884 # no non-identical "pretty close" pair
2025-07-02 05:52:20.889 if eqi is None:
2025-07-02 05:52:20.894 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:20.899 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:20.904 return
2025-07-02 05:52:20.910 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:20.917 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:20.929 else:
2025-07-02 05:52:20.942 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:20.955 eqi = None
2025-07-02 05:52:20.966
2025-07-02 05:52:20.974 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:20.980 # identical
2025-07-02 05:52:20.987
2025-07-02 05:52:20.994 # pump out diffs from before the synch point
2025-07-02 05:52:21.006 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:21.012
2025-07-02 05:52:21.020 # do intraline marking on the synch pair
2025-07-02 05:52:21.027 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:21.035 if eqi is None:
2025-07-02 05:52:21.043 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:21.051 atags = btags = ""
2025-07-02 05:52:21.065 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:21.074 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:21.082 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:21.089 if tag == 'replace':
2025-07-02 05:52:21.094 atags += '^' * la
2025-07-02 05:52:21.099 btags += '^' * lb
2025-07-02 05:52:21.103 elif tag == 'delete':
2025-07-02 05:52:21.108 atags += '-' * la
2025-07-02 05:52:21.112 elif tag == 'insert':
2025-07-02 05:52:21.119 btags += '+' * lb
2025-07-02 05:52:21.130 elif tag == 'equal':
2025-07-02 05:52:21.138 atags += ' ' * la
2025-07-02 05:52:21.144 btags += ' ' * lb
2025-07-02 05:52:21.153 else:
2025-07-02 05:52:21.166 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:21.175 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:21.188 else:
2025-07-02 05:52:21.200 # the synch pair is identical
2025-07-02 05:52:21.209 yield '  ' + aelt
2025-07-02 05:52:21.216
2025-07-02 05:52:21.224 # pump out diffs from after the synch point
2025-07-02 05:52:21.230 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:21.236
2025-07-02 05:52:21.242 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:21.253 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:21.264
2025-07-02 05:52:21.275 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:21.287 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:21.299 alo = 435, ahi = 1101
2025-07-02 05:52:21.312 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:21.323 blo = 435, bhi = 1101
2025-07-02 05:52:21.336
2025-07-02 05:52:21.347 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:21.357 g = []
2025-07-02 05:52:21.365 if alo < ahi:
2025-07-02 05:52:21.372 if blo < bhi:
2025-07-02 05:52:21.378 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:21.385 else:
2025-07-02 05:52:21.392 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:21.398 elif blo < bhi:
2025-07-02 05:52:21.406 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:21.419
2025-07-02 05:52:21.428 >       yield from g
2025-07-02 05:52:21.436
2025-07-02 05:52:21.443 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:21.450 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:21.460
2025-07-02 05:52:21.470 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:21.479 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:21.486 alo = 435, ahi = 1101
2025-07-02 05:52:21.492 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:21.498 blo = 435, bhi = 1101
2025-07-02 05:52:21.503
2025-07-02 05:52:21.509 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:21.514 r"""
2025-07-02 05:52:21.521 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:21.527 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:21.534 synch point, and intraline difference marking is done on the
2025-07-02 05:52:21.540 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:21.545
2025-07-02 05:52:21.551 Example:
2025-07-02 05:52:21.557
2025-07-02 05:52:21.569 >>> d = Differ()
2025-07-02 05:52:21.581 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:21.591 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:21.602 >>> print(''.join(results), end="")
2025-07-02 05:52:21.611 - abcDefghiJkl
2025-07-02 05:52:21.626 + abcdefGhijkl
2025-07-02 05:52:21.638 """
2025-07-02 05:52:21.644
2025-07-02 05:52:21.651 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:21.663 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:21.672 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:21.680 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:21.688 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:21.703
2025-07-02 05:52:21.714 # search for the pair that matches best without being identical
2025-07-02 05:52:21.722 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:21.731 # on junk -- unless we have to)
2025-07-02 05:52:21.742 for j in range(blo, bhi):
2025-07-02 05:52:21.751 bj = b[j]
2025-07-02 05:52:21.759 cruncher.set_seq2(bj)
2025-07-02 05:52:21.766 for i in range(alo, ahi):
2025-07-02 05:52:21.772 ai = a[i]
2025-07-02 05:52:21.778 if ai == bj:
2025-07-02 05:52:21.784 if eqi is None:
2025-07-02 05:52:21.793 eqi, eqj = i, j
2025-07-02 05:52:21.802 continue
2025-07-02 05:52:21.811 cruncher.set_seq1(ai)
2025-07-02 05:52:21.818 # computing similarity is expensive, so use the quick
2025-07-02 05:52:21.831 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:21.840 # compares by a factor of 3.
2025-07-02 05:52:21.849 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:21.856 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:21.866 # of the computation is cached by cruncher
2025-07-02 05:52:21.876 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:21.888 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:21.899 cruncher.ratio() > best_ratio:
2025-07-02 05:52:21.905 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:21.910 if best_ratio < cutoff:
2025-07-02 05:52:21.915 # no non-identical "pretty close" pair
2025-07-02 05:52:21.922 if eqi is None:
2025-07-02 05:52:21.930 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:21.942 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:21.953 return
2025-07-02 05:52:21.963 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:21.968 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:21.974 else:
2025-07-02 05:52:21.980 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:21.986 eqi = None
2025-07-02 05:52:21.990
2025-07-02 05:52:21.995 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:22.000 # identical
2025-07-02 05:52:22.005
2025-07-02 05:52:22.010 # pump out diffs from before the synch point
2025-07-02 05:52:22.014 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:22.020
2025-07-02 05:52:22.024 # do intraline marking on the synch pair
2025-07-02 05:52:22.029 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:22.035 if eqi is None:
2025-07-02 05:52:22.042 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:22.048 atags = btags = ""
2025-07-02 05:52:22.054 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:22.061 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:22.068 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:22.076 if tag == 'replace':
2025-07-02 05:52:22.084 atags += '^' * la
2025-07-02 05:52:22.089 btags += '^' * lb
2025-07-02 05:52:22.095 elif tag == 'delete':
2025-07-02 05:52:22.104 atags += '-' * la
2025-07-02 05:52:22.113 elif tag == 'insert':
2025-07-02 05:52:22.123 btags += '+' * lb
2025-07-02 05:52:22.134 elif tag == 'equal':
2025-07-02 05:52:22.143 atags += ' ' * la
2025-07-02 05:52:22.151 btags += ' ' * lb
2025-07-02 05:52:22.159 else:
2025-07-02 05:52:22.171 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:22.181 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:22.189 else:
2025-07-02 05:52:22.197 # the synch pair is identical
2025-07-02 05:52:22.204 yield '  ' + aelt
2025-07-02 05:52:22.211
2025-07-02 05:52:22.217 # pump out diffs from after the synch point
2025-07-02 05:52:22.224 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:22.230
2025-07-02 05:52:22.237 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:22.249 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:22.259
2025-07-02 05:52:22.266 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:22.274 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:22.282 alo = 436, ahi = 1101
2025-07-02 05:52:22.290 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:22.298 blo = 436, bhi = 1101
2025-07-02 05:52:22.305
2025-07-02 05:52:22.313 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:22.319 g = []
2025-07-02 05:52:22.325 if alo < ahi:
2025-07-02 05:52:22.331 if blo < bhi:
2025-07-02 05:52:22.337 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:22.343 else:
2025-07-02 05:52:22.349 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:22.357 elif blo < bhi:
2025-07-02 05:52:22.363 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:22.371
2025-07-02 05:52:22.378 >       yield from g
2025-07-02 05:52:22.392
2025-07-02 05:52:22.405 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:22.416 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:22.424
2025-07-02 05:52:22.430 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:22.436 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:22.443 alo = 436, ahi = 1101
2025-07-02 05:52:22.450 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:22.460 blo = 436, bhi = 1101
2025-07-02 05:52:22.468
2025-07-02 05:52:22.475 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:22.479 r"""
2025-07-02 05:52:22.485 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:22.490 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:22.501 synch point, and intraline difference marking is done on the
2025-07-02 05:52:22.510 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:22.521
2025-07-02 05:52:22.533 Example:
2025-07-02 05:52:22.544
2025-07-02 05:52:22.552 >>> d = Differ()
2025-07-02 05:52:22.561 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:22.568 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:22.576 >>> print(''.join(results), end="")
2025-07-02 05:52:22.582 - abcDefghiJkl
2025-07-02 05:52:22.595 + abcdefGhijkl
2025-07-02 05:52:22.606 """
2025-07-02 05:52:22.614
2025-07-02 05:52:22.627 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:22.638 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:22.649 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:22.659 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:22.668 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:22.676
2025-07-02 05:52:22.682 # search for the pair that matches best without being identical
2025-07-02 05:52:22.689 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:22.697 # on junk -- unless we have to)
2025-07-02 05:52:22.705 for j in range(blo, bhi):
2025-07-02 05:52:22.716 bj = b[j]
2025-07-02 05:52:22.729 cruncher.set_seq2(bj)
2025-07-02 05:52:22.739 for i in range(alo, ahi):
2025-07-02 05:52:22.746 ai = a[i]
2025-07-02 05:52:22.755 if ai == bj:
2025-07-02 05:52:22.763 if eqi is None:
2025-07-02 05:52:22.770 eqi, eqj = i, j
2025-07-02 05:52:22.779 continue
2025-07-02 05:52:22.791 cruncher.set_seq1(ai)
2025-07-02 05:52:22.803 # computing similarity is expensive, so use the quick
2025-07-02 05:52:22.814 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:22.821 # compares by a factor of 3.
2025-07-02 05:52:22.828 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:22.834 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:22.842 # of the computation is cached by cruncher
2025-07-02 05:52:22.851 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:22.858 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:22.871 cruncher.ratio() > best_ratio:
2025-07-02 05:52:22.884 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:22.895 if best_ratio < cutoff:
2025-07-02 05:52:22.903 # no non-identical "pretty close" pair
2025-07-02 05:52:22.911 if eqi is None:
2025-07-02 05:52:22.920 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:22.932 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:22.943 return
2025-07-02 05:52:22.952 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:22.960 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:22.967 else:
2025-07-02 05:52:22.974 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:22.986 eqi = None
2025-07-02 05:52:22.996
2025-07-02 05:52:23.004 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:23.012 # identical
2025-07-02 05:52:23.019
2025-07-02 05:52:23.027 # pump out diffs from before the synch point
2025-07-02 05:52:23.039 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:23.048
2025-07-02 05:52:23.056 # do intraline marking on the synch pair
2025-07-02 05:52:23.063 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:23.070 if eqi is None:
2025-07-02 05:52:23.077 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:23.091 atags = btags = ""
2025-07-02 05:52:23.105 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:23.115 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:23.124 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:23.131 if tag == 'replace':
2025-07-02 05:52:23.138 atags += '^' * la
2025-07-02 05:52:23.146 btags += '^' * lb
2025-07-02 05:52:23.152 elif tag == 'delete':
2025-07-02 05:52:23.159 atags += '-' * la
2025-07-02 05:52:23.166 elif tag == 'insert':
2025-07-02 05:52:23.176 btags += '+' * lb
2025-07-02 05:52:23.187 elif tag == 'equal':
2025-07-02 05:52:23.195 atags += ' ' * la
2025-07-02 05:52:23.203 btags += ' ' * lb
2025-07-02 05:52:23.210 else:
2025-07-02 05:52:23.219 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:23.227 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:23.235 else:
2025-07-02 05:52:23.243 # the synch pair is identical
2025-07-02 05:52:23.250 yield '  ' + aelt
2025-07-02 05:52:23.258
2025-07-02 05:52:23.266 # pump out diffs from after the synch point
2025-07-02 05:52:23.273 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:23.279
2025-07-02 05:52:23.286 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:23.295 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:23.307
2025-07-02 05:52:23.316 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:23.324 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:23.330 alo = 437, ahi = 1101
2025-07-02 05:52:23.338 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:23.344 blo = 437, bhi = 1101
2025-07-02 05:52:23.354
2025-07-02 05:52:23.370 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:23.385 g = []
2025-07-02 05:52:23.398 if alo < ahi:
2025-07-02 05:52:23.410 if blo < bhi:
2025-07-02 05:52:23.419 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:23.428 else:
2025-07-02 05:52:23.437 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:23.442 elif blo < bhi:
2025-07-02 05:52:23.455 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:23.464
2025-07-02 05:52:23.472 >       yield from g
2025-07-02 05:52:23.478
2025-07-02 05:52:23.487 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:23.496 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:23.506
2025-07-02 05:52:23.518 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:23.526 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:23.537 alo = 437, ahi = 1101
2025-07-02 05:52:23.549 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:23.555 blo = 437, bhi = 1101
2025-07-02 05:52:23.561
2025-07-02 05:52:23.566 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:23.571 r"""
2025-07-02 05:52:23.575 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:23.580 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:23.585 synch point, and intraline difference marking is done on the
2025-07-02 05:52:23.590 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:23.594
2025-07-02 05:52:23.599 Example:
2025-07-02 05:52:23.608
2025-07-02 05:52:23.620 >>> d = Differ()
2025-07-02 05:52:23.628 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:23.635 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:23.642 >>> print(''.join(results), end="")
2025-07-02 05:52:23.649 - abcDefghiJkl
2025-07-02 05:52:23.664 + abcdefGhijkl
2025-07-02 05:52:23.684 """
2025-07-02 05:52:23.692
2025-07-02 05:52:23.697 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:23.703 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:23.707 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:23.712 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:23.726 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:23.737
2025-07-02 05:52:23.749 # search for the pair that matches best without being identical
2025-07-02 05:52:23.758 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:23.765 # on junk -- unless we have to)
2025-07-02 05:52:23.771 for j in range(blo, bhi):
2025-07-02 05:52:23.778 bj = b[j]
2025-07-02 05:52:23.784 cruncher.set_seq2(bj)
2025-07-02 05:52:23.791 for i in range(alo, ahi):
2025-07-02 05:52:23.802 ai = a[i]
2025-07-02 05:52:23.811 if ai == bj:
2025-07-02 05:52:23.817 if eqi is None:
2025-07-02 05:52:23.822 eqi, eqj = i, j
2025-07-02 05:52:23.827 continue
2025-07-02 05:52:23.832 cruncher.set_seq1(ai)
2025-07-02 05:52:23.836 # computing similarity is expensive, so use the quick
2025-07-02 05:52:23.843 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:23.853 # compares by a factor of 3.
2025-07-02 05:52:23.860 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:23.867 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:23.873 # of the computation is cached by cruncher
2025-07-02 05:52:23.878 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:23.883 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:23.888 cruncher.ratio() > best_ratio:
2025-07-02 05:52:23.893 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:23.899 if best_ratio < cutoff:
2025-07-02 05:52:23.904 # no non-identical "pretty close" pair
2025-07-02 05:52:23.910 if eqi is None:
2025-07-02 05:52:23.919 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:23.925 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:23.932 return
2025-07-02 05:52:23.940 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:23.946 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:23.956 else:
2025-07-02 05:52:23.965 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:23.972 eqi = None
2025-07-02 05:52:23.979
2025-07-02 05:52:23.986 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:23.993 # identical
2025-07-02 05:52:23.999
2025-07-02 05:52:24.013 # pump out diffs from before the synch point
2025-07-02 05:52:24.024 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:24.038
2025-07-02 05:52:24.049 # do intraline marking on the synch pair
2025-07-02 05:52:24.061 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:24.073 if eqi is None:
2025-07-02 05:52:24.086 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:24.098 atags = btags = ""
2025-07-02 05:52:24.110 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:24.121 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:24.132 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:24.141 if tag == 'replace':
2025-07-02 05:52:24.149 atags += '^' * la
2025-07-02 05:52:24.156 btags += '^' * lb
2025-07-02 05:52:24.165 elif tag == 'delete':
2025-07-02 05:52:24.179 atags += '-' * la
2025-07-02 05:52:24.192 elif tag == 'insert':
2025-07-02 05:52:24.203 btags += '+' * lb
2025-07-02 05:52:24.214 elif tag == 'equal':
2025-07-02 05:52:24.224 atags += ' ' * la
2025-07-02 05:52:24.232 btags += ' ' * lb
2025-07-02 05:52:24.240 else:
2025-07-02 05:52:24.247 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:24.254 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:24.263 else:
2025-07-02 05:52:24.271 # the synch pair is identical
2025-07-02 05:52:24.277 yield '  ' + aelt
2025-07-02 05:52:24.283
2025-07-02 05:52:24.291 # pump out diffs from after the synch point
2025-07-02 05:52:24.298 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:24.305
2025-07-02 05:52:24.312 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:24.320 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:24.326
2025-07-02 05:52:24.336 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:24.348 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:24.359 alo = 438, ahi = 1101
2025-07-02 05:52:24.369 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:24.378 blo = 438, bhi = 1101
2025-07-02 05:52:24.386
2025-07-02 05:52:24.394 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:24.408 g = []
2025-07-02 05:52:24.421 if alo < ahi:
2025-07-02 05:52:24.430 if blo < bhi:
2025-07-02 05:52:24.436 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:24.444 else:
2025-07-02 05:52:24.452 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:24.458 elif blo < bhi:
2025-07-02 05:52:24.469 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:24.479
2025-07-02 05:52:24.487 >       yield from g
2025-07-02 05:52:24.495
2025-07-02 05:52:24.507 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:24.519 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:24.532
2025-07-02 05:52:24.544 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:24.555 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:24.564 alo = 438, ahi = 1101
2025-07-02 05:52:24.577 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:24.589 blo = 438, bhi = 1101
2025-07-02 05:52:24.597
2025-07-02 05:52:24.609 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:24.620 r"""
2025-07-02 05:52:24.630 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:24.637 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:24.649 synch point, and intraline difference marking is done on the
2025-07-02 05:52:24.660 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:24.668
2025-07-02 05:52:24.675 Example:
2025-07-02 05:52:24.680
2025-07-02 05:52:24.685 >>> d = Differ()
2025-07-02 05:52:24.691 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:24.698 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:24.704 >>> print(''.join(results), end="")
2025-07-02 05:52:24.709 - abcDefghiJkl
2025-07-02 05:52:24.719 + abcdefGhijkl
2025-07-02 05:52:24.728 """
2025-07-02 05:52:24.732
2025-07-02 05:52:24.737 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:24.742 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:24.746 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:24.751 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:24.755 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:24.762
2025-07-02 05:52:24.768 # search for the pair that matches best without being identical
2025-07-02 05:52:24.774 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:24.783 # on junk -- unless we have to)
2025-07-02 05:52:24.790 for j in range(blo, bhi):
2025-07-02 05:52:24.798 bj = b[j]
2025-07-02 05:52:24.806 cruncher.set_seq2(bj)
2025-07-02 05:52:24.815 for i in range(alo, ahi):
2025-07-02 05:52:24.824 ai = a[i]
2025-07-02 05:52:24.832 if ai == bj:
2025-07-02 05:52:24.841 if eqi is None:
2025-07-02 05:52:24.849 eqi, eqj = i, j
2025-07-02 05:52:24.855 continue
2025-07-02 05:52:24.861 cruncher.set_seq1(ai)
2025-07-02 05:52:24.866 # computing similarity is expensive, so use the quick
2025-07-02 05:52:24.872 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:24.878 # compares by a factor of 3.
2025-07-02 05:52:24.885 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:24.891 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:24.896 # of the computation is cached by cruncher
2025-07-02 05:52:24.901 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:24.907 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:24.913 cruncher.ratio() > best_ratio:
2025-07-02 05:52:24.919 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:24.925 if best_ratio < cutoff:
2025-07-02 05:52:24.931 # no non-identical "pretty close" pair
2025-07-02 05:52:24.936 if eqi is None:
2025-07-02 05:52:24.942 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:24.948 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:24.954 return
2025-07-02 05:52:24.964 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:24.974 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:24.985 else:
2025-07-02 05:52:24.996 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:25.007 eqi = None
2025-07-02 05:52:25.015
2025-07-02 05:52:25.023 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:25.036 # identical
2025-07-02 05:52:25.044
2025-07-02 05:52:25.052 # pump out diffs from before the synch point
2025-07-02 05:52:25.060 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:25.066
2025-07-02 05:52:25.079 # do intraline marking on the synch pair
2025-07-02 05:52:25.092 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:25.104 if eqi is None:
2025-07-02 05:52:25.117 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:25.127 atags = btags = ""
2025-07-02 05:52:25.136 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:25.144 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:25.151 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:25.165 if tag == 'replace':
2025-07-02 05:52:25.178 atags += '^' * la
2025-07-02 05:52:25.186 btags += '^' * lb
2025-07-02 05:52:25.197 elif tag == 'delete':
2025-07-02 05:52:25.210 atags += '-' * la
2025-07-02 05:52:25.219 elif tag == 'insert':
2025-07-02 05:52:25.226 btags += '+' * lb
2025-07-02 05:52:25.233 elif tag == 'equal':
2025-07-02 05:52:25.239 atags += ' ' * la
2025-07-02 05:52:25.245 btags += ' ' * lb
2025-07-02 05:52:25.251 else:
2025-07-02 05:52:25.257 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:25.263 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:25.269 else:
2025-07-02 05:52:25.277 # the synch pair is identical
2025-07-02 05:52:25.285 yield '  ' + aelt
2025-07-02 05:52:25.291
2025-07-02 05:52:25.298 # pump out diffs from after the synch point
2025-07-02 05:52:25.304 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:25.310
2025-07-02 05:52:25.317 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:25.329 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:25.338
2025-07-02 05:52:25.347 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:25.354 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:25.366 alo = 439, ahi = 1101
2025-07-02 05:52:25.382 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:25.394 blo = 439, bhi = 1101
2025-07-02 05:52:25.408
2025-07-02 05:52:25.418 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:25.427 g = []
2025-07-02 05:52:25.436 if alo < ahi:
2025-07-02 05:52:25.446 if blo < bhi:
2025-07-02 05:52:25.458 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:25.468 else:
2025-07-02 05:52:25.476 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:25.484 elif blo < bhi:
2025-07-02 05:52:25.491 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:25.503
2025-07-02 05:52:25.513 >       yield from g
2025-07-02 05:52:25.524
2025-07-02 05:52:25.534 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:25.546 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:25.558
2025-07-02 05:52:25.569 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:25.578 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:25.584 alo = 439, ahi = 1101
2025-07-02 05:52:25.592 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:25.598 blo = 439, bhi = 1101
2025-07-02 05:52:25.604
2025-07-02 05:52:25.610 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:25.615 r"""
2025-07-02 05:52:25.620 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:25.626 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:25.634 synch point, and intraline difference marking is done on the
2025-07-02 05:52:25.641 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:25.648
2025-07-02 05:52:25.654 Example:
2025-07-02 05:52:25.664
2025-07-02 05:52:25.672 >>> d = Differ()
2025-07-02 05:52:25.680 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:25.687 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:25.694 >>> print(''.join(results), end="")
2025-07-02 05:52:25.699 - abcDefghiJkl
2025-07-02 05:52:25.711 + abcdefGhijkl
2025-07-02 05:52:25.721 """
2025-07-02 05:52:25.727
2025-07-02 05:52:25.733 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:25.740 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:25.746 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:25.752 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:25.759 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:25.767
2025-07-02 05:52:25.778 # search for the pair that matches best without being identical
2025-07-02 05:52:25.791 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:25.804 # on junk -- unless we have to)
2025-07-02 05:52:25.817 for j in range(blo, bhi):
2025-07-02 05:52:25.826 bj = b[j]
2025-07-02 05:52:25.840 cruncher.set_seq2(bj)
2025-07-02 05:52:25.848 for i in range(alo, ahi):
2025-07-02 05:52:25.855 ai = a[i]
2025-07-02 05:52:25.861 if ai == bj:
2025-07-02 05:52:25.867 if eqi is None:
2025-07-02 05:52:25.874 eqi, eqj = i, j
2025-07-02 05:52:25.888 continue
2025-07-02 05:52:25.896 cruncher.set_seq1(ai)
2025-07-02 05:52:25.904 # computing similarity is expensive, so use the quick
2025-07-02 05:52:25.911 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:25.918 # compares by a factor of 3.
2025-07-02 05:52:25.925 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:25.931 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:25.938 # of the computation is cached by cruncher
2025-07-02 05:52:25.947 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:25.958 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:25.971 cruncher.ratio() > best_ratio:
2025-07-02 05:52:25.980 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:25.993 if best_ratio < cutoff:
2025-07-02 05:52:26.001 # no non-identical "pretty close" pair
2025-07-02 05:52:26.008 if eqi is None:
2025-07-02 05:52:26.014 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:26.020 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:26.027 return
2025-07-02 05:52:26.035 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:26.043 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:26.050 else:
2025-07-02 05:52:26.058 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:26.069 eqi = None
2025-07-02 05:52:26.079
2025-07-02 05:52:26.092 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:26.105 # identical
2025-07-02 05:52:26.119
2025-07-02 05:52:26.128 # pump out diffs from before the synch point
2025-07-02 05:52:26.137 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:26.146
2025-07-02 05:52:26.157 # do intraline marking on the synch pair
2025-07-02 05:52:26.169 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:26.180 if eqi is None:
2025-07-02 05:52:26.187 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:26.194 atags = btags = ""
2025-07-02 05:52:26.205 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:26.215 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:26.224 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:26.232 if tag == 'replace':
2025-07-02 05:52:26.239 atags += '^' * la
2025-07-02 05:52:26.245 btags += '^' * lb
2025-07-02 05:52:26.256 elif tag == 'delete':
2025-07-02 05:52:26.265 atags += '-' * la
2025-07-02 05:52:26.275 elif tag == 'insert':
2025-07-02 05:52:26.282 btags += '+' * lb
2025-07-02 05:52:26.289 elif tag == 'equal':
2025-07-02 05:52:26.296 atags += ' ' * la
2025-07-02 05:52:26.301 btags += ' ' * lb
2025-07-02 05:52:26.307 else:
2025-07-02 05:52:26.313 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:26.320 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:26.327 else:
2025-07-02 05:52:26.333 # the synch pair is identical
2025-07-02 05:52:26.339 yield '  ' + aelt
2025-07-02 05:52:26.345
2025-07-02 05:52:26.350 # pump out diffs from after the synch point
2025-07-02 05:52:26.356 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:26.365
2025-07-02 05:52:26.373 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:26.379 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:26.386
2025-07-02 05:52:26.392 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:26.400 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:26.407 alo = 440, ahi = 1101
2025-07-02 05:52:26.414 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:26.423 blo = 440, bhi = 1101
2025-07-02 05:52:26.436
2025-07-02 05:52:26.444 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:26.451 g = []
2025-07-02 05:52:26.458 if alo < ahi:
2025-07-02 05:52:26.464 if blo < bhi:
2025-07-02 05:52:26.474 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:26.486 else:
2025-07-02 05:52:26.500 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:26.512 elif blo < bhi:
2025-07-02 05:52:26.520 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:26.527
2025-07-02 05:52:26.533 >       yield from g
2025-07-02 05:52:26.538
2025-07-02 05:52:26.543 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:26.553 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:26.559
2025-07-02 05:52:26.566 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:26.574 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:26.586 alo = 440, ahi = 1101
2025-07-02 05:52:26.594 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:26.606 blo = 440, bhi = 1101
2025-07-02 05:52:26.617
2025-07-02 05:52:26.628 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:26.640 r"""
2025-07-02 05:52:26.651 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:26.659 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:26.665 synch point, and intraline difference marking is done on the
2025-07-02 05:52:26.671 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:26.675
2025-07-02 05:52:26.680 Example:
2025-07-02 05:52:26.684
2025-07-02 05:52:26.691 >>> d = Differ()
2025-07-02 05:52:26.697 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:26.704 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:26.711 >>> print(''.join(results), end="")
2025-07-02 05:52:26.719 - abcDefghiJkl
2025-07-02 05:52:26.737 + abcdefGhijkl
2025-07-02 05:52:26.753 """
2025-07-02 05:52:26.761
2025-07-02 05:52:26.767 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:26.774 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:26.781 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:26.787 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:26.794 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:26.801
2025-07-02 05:52:26.807 # search for the pair that matches best without being identical
2025-07-02 05:52:26.813 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:26.819 # on junk -- unless we have to)
2025-07-02 05:52:26.824 for j in range(blo, bhi):
2025-07-02 05:52:26.830 bj = b[j]
2025-07-02 05:52:26.836 cruncher.set_seq2(bj)
2025-07-02 05:52:26.842 for i in range(alo, ahi):
2025-07-02 05:52:26.847 ai = a[i]
2025-07-02 05:52:26.852 if ai == bj:
2025-07-02 05:52:26.857 if eqi is None:
2025-07-02 05:52:26.871 eqi, eqj = i, j
2025-07-02 05:52:26.884 continue
2025-07-02 05:52:26.895 cruncher.set_seq1(ai)
2025-07-02 05:52:26.907 # computing similarity is expensive, so use the quick
2025-07-02 05:52:26.917 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:26.928 # compares by a factor of 3.
2025-07-02 05:52:26.937 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:26.945 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:26.957 # of the computation is cached by cruncher
2025-07-02 05:52:26.966 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:26.973 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:26.985 cruncher.ratio() > best_ratio:
2025-07-02 05:52:26.999 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:27.008 if best_ratio < cutoff:
2025-07-02 05:52:27.014 # no non-identical "pretty close" pair
2025-07-02 05:52:27.023 if eqi is None:
2025-07-02 05:52:27.031 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:27.039 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:27.048 return
2025-07-02 05:52:27.056 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:27.067 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:27.081 else:
2025-07-02 05:52:27.088 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:27.096 eqi = None
2025-07-02 05:52:27.108
2025-07-02 05:52:27.122 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:27.131 # identical
2025-07-02 05:52:27.139
2025-07-02 05:52:27.147 # pump out diffs from before the synch point
2025-07-02 05:52:27.154 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:27.165
2025-07-02 05:52:27.176 # do intraline marking on the synch pair
2025-07-02 05:52:27.185 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:27.192 if eqi is None:
2025-07-02 05:52:27.199 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:27.205 atags = btags = ""
2025-07-02 05:52:27.212 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:27.218 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:27.226 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:27.236 if tag == 'replace':
2025-07-02 05:52:27.245 atags += '^' * la
2025-07-02 05:52:27.254 btags += '^' * lb
2025-07-02 05:52:27.261 elif tag == 'delete':
2025-07-02 05:52:27.267 atags += '-' * la
2025-07-02 05:52:27.272 elif tag == 'insert':
2025-07-02 05:52:27.277 btags += '+' * lb
2025-07-02 05:52:27.282 elif tag == 'equal':
2025-07-02 05:52:27.289 atags += ' ' * la
2025-07-02 05:52:27.295 btags += ' ' * lb
2025-07-02 05:52:27.301 else:
2025-07-02 05:52:27.308 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:27.314 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:27.325 else:
2025-07-02 05:52:27.335 # the synch pair is identical
2025-07-02 05:52:27.344 yield '  ' + aelt
2025-07-02 05:52:27.351
2025-07-02 05:52:27.359 # pump out diffs from after the synch point
2025-07-02 05:52:27.376 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:27.386
2025-07-02 05:52:27.400 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:27.411 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:27.420
2025-07-02 05:52:27.427 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:27.435 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:27.442 alo = 441, ahi = 1101
2025-07-02 05:52:27.450 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:27.457 blo = 441, bhi = 1101
2025-07-02 05:52:27.463
2025-07-02 05:52:27.470 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:27.480 g = []
2025-07-02 05:52:27.490 if alo < ahi:
2025-07-02 05:52:27.500 if blo < bhi:
2025-07-02 05:52:27.507 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:27.514 else:
2025-07-02 05:52:27.526 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:27.539 elif blo < bhi:
2025-07-02 05:52:27.549 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:27.558
2025-07-02 05:52:27.566 >       yield from g
2025-07-02 05:52:27.576
2025-07-02 05:52:27.586 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:27.595 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:27.603
2025-07-02 05:52:27.609 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:27.617 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:27.623 alo = 441, ahi = 1101
2025-07-02 05:52:27.638 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:27.649 blo = 441, bhi = 1101
2025-07-02 05:52:27.663
2025-07-02 05:52:27.674 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:27.681 r"""
2025-07-02 05:52:27.694 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:27.703 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:27.710 synch point, and intraline difference marking is done on the
2025-07-02 05:52:27.717 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:27.722
2025-07-02 05:52:27.726 Example:
2025-07-02 05:52:27.731
2025-07-02 05:52:27.747 >>> d = Differ()
2025-07-02 05:52:27.756 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:27.763 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:27.774 >>> print(''.join(results), end="")
2025-07-02 05:52:27.785 - abcDefghiJkl
2025-07-02 05:52:27.801 + abcdefGhijkl
2025-07-02 05:52:27.814 """
2025-07-02 05:52:27.820
2025-07-02 05:52:27.827 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:27.836 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:27.845 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:27.852 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:27.860 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:27.867
2025-07-02 05:52:27.876 # search for the pair that matches best without being identical
2025-07-02 05:52:27.882 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:27.895 # on junk -- unless we have to)
2025-07-02 05:52:27.905 for j in range(blo, bhi):
2025-07-02 05:52:27.913 bj = b[j]
2025-07-02 05:52:27.920 cruncher.set_seq2(bj)
2025-07-02 05:52:27.933 for i in range(alo, ahi):
2025-07-02 05:52:27.944 ai = a[i]
2025-07-02 05:52:27.954 if ai == bj:
2025-07-02 05:52:27.962 if eqi is None:
2025-07-02 05:52:27.970 eqi, eqj = i, j
2025-07-02 05:52:27.975 continue
2025-07-02 05:52:27.981 cruncher.set_seq1(ai)
2025-07-02 05:52:27.987 # computing similarity is expensive, so use the quick
2025-07-02 05:52:27.994 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:28.004 # compares by a factor of 3.
2025-07-02 05:52:28.016 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:28.025 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:28.031 # of the computation is cached by cruncher
2025-07-02 05:52:28.036 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:28.043 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:28.048 cruncher.ratio() > best_ratio:
2025-07-02 05:52:28.060 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:28.070 if best_ratio < cutoff:
2025-07-02 05:52:28.080 # no non-identical "pretty close" pair
2025-07-02 05:52:28.090 if eqi is None:
2025-07-02 05:52:28.097 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:28.109 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:28.121 return
2025-07-02 05:52:28.131 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:28.142 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:28.155 else:
2025-07-02 05:52:28.166 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:28.179 eqi = None
2025-07-02 05:52:28.190
2025-07-02 05:52:28.199 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:28.207 # identical
2025-07-02 05:52:28.214
2025-07-02 05:52:28.227 # pump out diffs from before the synch point
2025-07-02 05:52:28.242 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:28.252
2025-07-02 05:52:28.261 # do intraline marking on the synch pair
2025-07-02 05:52:28.269 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:28.275 if eqi is None:
2025-07-02 05:52:28.283 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:28.292 atags = btags = ""
2025-07-02 05:52:28.301 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:28.308 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:28.316 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:28.323 if tag == 'replace':
2025-07-02 05:52:28.338 atags += '^' * la
2025-07-02 05:52:28.350 btags += '^' * lb
2025-07-02 05:52:28.359 elif tag == 'delete':
2025-07-02 05:52:28.371 atags += '-' * la
2025-07-02 05:52:28.385 elif tag == 'insert':
2025-07-02 05:52:28.399 btags += '+' * lb
2025-07-02 05:52:28.406 elif tag == 'equal':
2025-07-02 05:52:28.414 atags += ' ' * la
2025-07-02 05:52:28.423 btags += ' ' * lb
2025-07-02 05:52:28.435 else:
2025-07-02 05:52:28.444 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:28.451 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:28.457 else:
2025-07-02 05:52:28.463 # the synch pair is identical
2025-07-02 05:52:28.470 yield '  ' + aelt
2025-07-02 05:52:28.477
2025-07-02 05:52:28.486 # pump out diffs from after the synch point
2025-07-02 05:52:28.499 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:28.509
2025-07-02 05:52:28.517 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:28.526 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:28.533
2025-07-02 05:52:28.544 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:28.554 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:28.560 alo = 444, ahi = 1101
2025-07-02 05:52:28.567 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:28.572 blo = 444, bhi = 1101
2025-07-02 05:52:28.578
2025-07-02 05:52:28.585 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:28.591 g = []
2025-07-02 05:52:28.599 if alo < ahi:
2025-07-02 05:52:28.611 if blo < bhi:
2025-07-02 05:52:28.620 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:28.627 else:
2025-07-02 05:52:28.637 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:28.649 elif blo < bhi:
2025-07-02 05:52:28.660 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:28.673
2025-07-02 05:52:28.685 >       yield from g
2025-07-02 05:52:28.693
2025-07-02 05:52:28.703 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:28.709 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:28.715
2025-07-02 05:52:28.729 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:28.740 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:28.748 alo = 444, ahi = 1101
2025-07-02 05:52:28.761 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:28.770 blo = 444, bhi = 1101
2025-07-02 05:52:28.779
2025-07-02 05:52:28.787 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:28.795 r"""
2025-07-02 05:52:28.802 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:28.808 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:28.815 synch point, and intraline difference marking is done on the
2025-07-02 05:52:28.821 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:28.827
2025-07-02 05:52:28.833 Example:
2025-07-02 05:52:28.838
2025-07-02 05:52:28.849 >>> d = Differ()
2025-07-02 05:52:28.857 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:28.864 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:28.870 >>> print(''.join(results), end="")
2025-07-02 05:52:28.877 - abcDefghiJkl
2025-07-02 05:52:28.889 + abcdefGhijkl
2025-07-02 05:52:28.900 """
2025-07-02 05:52:28.905
2025-07-02 05:52:28.920 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:28.933 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:28.941 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:28.947 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:28.953 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:28.959
2025-07-02 05:52:28.964 # search for the pair that matches best without being identical
2025-07-02 05:52:28.971 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:28.977 # on junk -- unless we have to)
2025-07-02 05:52:28.983 for j in range(blo, bhi):
2025-07-02 05:52:28.991 bj = b[j]
2025-07-02 05:52:29.004 cruncher.set_seq2(bj)
2025-07-02 05:52:29.013 for i in range(alo, ahi):
2025-07-02 05:52:29.020 ai = a[i]
2025-07-02 05:52:29.027 if ai == bj:
2025-07-02 05:52:29.035 if eqi is None:
2025-07-02 05:52:29.042 eqi, eqj = i, j
2025-07-02 05:52:29.051 continue
2025-07-02 05:52:29.064 cruncher.set_seq1(ai)
2025-07-02 05:52:29.074 # computing similarity is expensive, so use the quick
2025-07-02 05:52:29.079 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:29.085 # compares by a factor of 3.
2025-07-02 05:52:29.090 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:29.099 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:29.108 # of the computation is cached by cruncher
2025-07-02 05:52:29.118 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:29.127 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:29.140 cruncher.ratio() > best_ratio:
2025-07-02 05:52:29.152 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:29.159 if best_ratio < cutoff:
2025-07-02 05:52:29.166 # no non-identical "pretty close" pair
2025-07-02 05:52:29.172 if eqi is None:
2025-07-02 05:52:29.179 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:29.187 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:29.192 return
2025-07-02 05:52:29.198 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:29.209 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:29.219 else:
2025-07-02 05:52:29.226 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:29.235 eqi = None
2025-07-02 05:52:29.248
2025-07-02 05:52:29.257 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:29.266 # identical
2025-07-02 05:52:29.277
2025-07-02 05:52:29.287 # pump out diffs from before the synch point
2025-07-02 05:52:29.295 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:29.304
2025-07-02 05:52:29.315 # do intraline marking on the synch pair
2025-07-02 05:52:29.325 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:29.332 if eqi is None:
2025-07-02 05:52:29.340 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:29.346 atags = btags = ""
2025-07-02 05:52:29.353 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:29.360 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:29.367 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:29.375 if tag == 'replace':
2025-07-02 05:52:29.387 atags += '^' * la
2025-07-02 05:52:29.396 btags += '^' * lb
2025-07-02 05:52:29.403 elif tag == 'delete':
2025-07-02 05:52:29.410 atags += '-' * la
2025-07-02 05:52:29.422 elif tag == 'insert':
2025-07-02 05:52:29.435 btags += '+' * lb
2025-07-02 05:52:29.446 elif tag == 'equal':
2025-07-02 05:52:29.458 atags += ' ' * la
2025-07-02 05:52:29.467 btags += ' ' * lb
2025-07-02 05:52:29.476 else:
2025-07-02 05:52:29.485 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:29.495 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:29.508 else:
2025-07-02 05:52:29.518 # the synch pair is identical
2025-07-02 05:52:29.525 yield '  ' + aelt
2025-07-02 05:52:29.532
2025-07-02 05:52:29.541 # pump out diffs from after the synch point
2025-07-02 05:52:29.553 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:29.564
2025-07-02 05:52:29.573 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:29.587 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:29.598
2025-07-02 05:52:29.607 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:29.616 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:29.629 alo = 445, ahi = 1101
2025-07-02 05:52:29.640 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:29.648 blo = 445, bhi = 1101
2025-07-02 05:52:29.655
2025-07-02 05:52:29.661 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:29.668 g = []
2025-07-02 05:52:29.676 if alo < ahi:
2025-07-02 05:52:29.683 if blo < bhi:
2025-07-02 05:52:29.691 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:29.699 else:
2025-07-02 05:52:29.707 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:29.715 elif blo < bhi:
2025-07-02 05:52:29.724 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:29.731
2025-07-02 05:52:29.738 >       yield from g
2025-07-02 05:52:29.743
2025-07-02 05:52:29.748 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:29.753 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:29.760
2025-07-02 05:52:29.768 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:29.782 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:29.791 alo = 445, ahi = 1101
2025-07-02 05:52:29.799 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:29.806 blo = 445, bhi = 1101
2025-07-02 05:52:29.812
2025-07-02 05:52:29.822 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:29.832 r"""
2025-07-02 05:52:29.841 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:29.848 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:29.855 synch point, and intraline difference marking is done on the
2025-07-02 05:52:29.863 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:29.869
2025-07-02 05:52:29.874 Example:
2025-07-02 05:52:29.879
2025-07-02 05:52:29.887 >>> d = Differ()
2025-07-02 05:52:29.899 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:29.907 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:29.915 >>> print(''.join(results), end="")
2025-07-02 05:52:29.926 - abcDefghiJkl
2025-07-02 05:52:29.950 + abcdefGhijkl
2025-07-02 05:52:29.977 """
2025-07-02 05:52:29.988
2025-07-02 05:52:29.999 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:30.008 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:30.015 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:30.023 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:30.031 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:30.038
2025-07-02 05:52:30.048 # search for the pair that matches best without being identical
2025-07-02 05:52:30.059 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:30.068 # on junk -- unless we have to)
2025-07-02 05:52:30.075 for j in range(blo, bhi):
2025-07-02 05:52:30.081 bj = b[j]
2025-07-02 05:52:30.086 cruncher.set_seq2(bj)
2025-07-02 05:52:30.091 for i in range(alo, ahi):
2025-07-02 05:52:30.096 ai = a[i]
2025-07-02 05:52:30.101 if ai == bj:
2025-07-02 05:52:30.107 if eqi is None:
2025-07-02 05:52:30.114 eqi, eqj = i, j
2025-07-02 05:52:30.120 continue
2025-07-02 05:52:30.126 cruncher.set_seq1(ai)
2025-07-02 05:52:30.137 # computing similarity is expensive, so use the quick
2025-07-02 05:52:30.147 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:30.154 # compares by a factor of 3.
2025-07-02 05:52:30.160 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:30.168 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:30.176 # of the computation is cached by cruncher
2025-07-02 05:52:30.183 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:30.191 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:30.199 cruncher.ratio() > best_ratio:
2025-07-02 05:52:30.207 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:30.214 if best_ratio < cutoff:
2025-07-02 05:52:30.222 # no non-identical "pretty close" pair
2025-07-02 05:52:30.229 if eqi is None:
2025-07-02 05:52:30.235 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:30.241 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:30.247 return
2025-07-02 05:52:30.254 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:30.263 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:30.272 else:
2025-07-02 05:52:30.281 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:30.287 eqi = None
2025-07-02 05:52:30.295
2025-07-02 05:52:30.302 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:30.315 # identical
2025-07-02 05:52:30.326
2025-07-02 05:52:30.334 # pump out diffs from before the synch point
2025-07-02 05:52:30.341 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:30.347
2025-07-02 05:52:30.361 # do intraline marking on the synch pair
2025-07-02 05:52:30.367 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:30.372 if eqi is None:
2025-07-02 05:52:30.377 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:30.383 atags = btags = ""
2025-07-02 05:52:30.391 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:30.403 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:30.410 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:30.417 if tag == 'replace':
2025-07-02 05:52:30.423 atags += '^' * la
2025-07-02 05:52:30.429 btags += '^' * lb
2025-07-02 05:52:30.436 elif tag == 'delete':
2025-07-02 05:52:30.443 atags += '-' * la
2025-07-02 05:52:30.450 elif tag == 'insert':
2025-07-02 05:52:30.459 btags += '+' * lb
2025-07-02 05:52:30.468 elif tag == 'equal':
2025-07-02 05:52:30.475 atags += ' ' * la
2025-07-02 05:52:30.481 btags += ' ' * lb
2025-07-02 05:52:30.488 else:
2025-07-02 05:52:30.494 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:30.499 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:30.504 else:
2025-07-02 05:52:30.510 # the synch pair is identical
2025-07-02 05:52:30.518 yield '  ' + aelt
2025-07-02 05:52:30.524
2025-07-02 05:52:30.530 # pump out diffs from after the synch point
2025-07-02 05:52:30.538 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:30.545
2025-07-02 05:52:30.556 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:30.566 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:30.577
2025-07-02 05:52:30.586 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:30.593 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:30.602 alo = 446, ahi = 1101
2025-07-02 05:52:30.609 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:30.615 blo = 446, bhi = 1101
2025-07-02 05:52:30.622
2025-07-02 05:52:30.629 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:30.640 g = []
2025-07-02 05:52:30.651 if alo < ahi:
2025-07-02 05:52:30.665 if blo < bhi:
2025-07-02 05:52:30.677 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:30.686 else:
2025-07-02 05:52:30.694 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:30.701 elif blo < bhi:
2025-07-02 05:52:30.708 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:30.715
2025-07-02 05:52:30.722 >       yield from g
2025-07-02 05:52:30.729
2025-07-02 05:52:30.736 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:30.743 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:30.750
2025-07-02 05:52:30.758 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:30.772 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:30.783 alo = 446, ahi = 1101
2025-07-02 05:52:30.793 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:30.802 blo = 446, bhi = 1101
2025-07-02 05:52:30.808
2025-07-02 05:52:30.814 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:30.820 r"""
2025-07-02 05:52:30.826 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:30.836 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:30.845 synch point, and intraline difference marking is done on the
2025-07-02 05:52:30.851 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:30.857
2025-07-02 05:52:30.862 Example:
2025-07-02 05:52:30.868
2025-07-02 05:52:30.873 >>> d = Differ()
2025-07-02 05:52:30.879 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:30.886 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:30.897 >>> print(''.join(results), end="")
2025-07-02 05:52:30.907 - abcDefghiJkl
2025-07-02 05:52:30.921 + abcdefGhijkl
2025-07-02 05:52:30.932 """
2025-07-02 05:52:30.939
2025-07-02 05:52:30.947 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:30.954 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:30.961 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:30.968 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:30.974 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:30.979
2025-07-02 05:52:30.987 # search for the pair that matches best without being identical
2025-07-02 05:52:30.993 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:30.998 # on junk -- unless we have to)
2025-07-02 05:52:31.003 for j in range(blo, bhi):
2025-07-02 05:52:31.009 bj = b[j]
2025-07-02 05:52:31.015 cruncher.set_seq2(bj)
2025-07-02 05:52:31.026 for i in range(alo, ahi):
2025-07-02 05:52:31.038 ai = a[i]
2025-07-02 05:52:31.047 if ai == bj:
2025-07-02 05:52:31.060 if eqi is None:
2025-07-02 05:52:31.069 eqi, eqj = i, j
2025-07-02 05:52:31.077 continue
2025-07-02 05:52:31.089 cruncher.set_seq1(ai)
2025-07-02 05:52:31.099 # computing similarity is expensive, so use the quick
2025-07-02 05:52:31.108 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:31.115 # compares by a factor of 3.
2025-07-02 05:52:31.122 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:31.128 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:31.134 # of the computation is cached by cruncher
2025-07-02 05:52:31.140 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:31.150 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:31.161 cruncher.ratio() > best_ratio:
2025-07-02 05:52:31.171 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:31.180 if best_ratio < cutoff:
2025-07-02 05:52:31.192 # no non-identical "pretty close" pair
2025-07-02 05:52:31.201 if eqi is None:
2025-07-02 05:52:31.209 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:31.215 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:31.222 return
2025-07-02 05:52:31.228 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:31.236 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:31.243 else:
2025-07-02 05:52:31.251 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:31.258 eqi = None
2025-07-02 05:52:31.268
2025-07-02 05:52:31.278 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:31.286 # identical
2025-07-02 05:52:31.297
2025-07-02 05:52:31.306 # pump out diffs from before the synch point
2025-07-02 05:52:31.313 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:31.324
2025-07-02 05:52:31.337 # do intraline marking on the synch pair
2025-07-02 05:52:31.349 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:31.356 if eqi is None:
2025-07-02 05:52:31.362 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:31.367 atags = btags = ""
2025-07-02 05:52:31.374 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:31.379 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:31.387 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:31.394 if tag == 'replace':
2025-07-02 05:52:31.401 atags += '^' * la
2025-07-02 05:52:31.410 btags += '^' * lb
2025-07-02 05:52:31.423 elif tag == 'delete':
2025-07-02 05:52:31.436 atags += '-' * la
2025-07-02 05:52:31.445 elif tag == 'insert':
2025-07-02 05:52:31.453 btags += '+' * lb
2025-07-02 05:52:31.458 elif tag == 'equal':
2025-07-02 05:52:31.463 atags += ' ' * la
2025-07-02 05:52:31.468 btags += ' ' * lb
2025-07-02 05:52:31.472 else:
2025-07-02 05:52:31.477 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:31.482 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:31.487 else:
2025-07-02 05:52:31.492 # the synch pair is identical
2025-07-02 05:52:31.498 yield '  ' + aelt
2025-07-02 05:52:31.503
2025-07-02 05:52:31.510 # pump out diffs from after the synch point
2025-07-02 05:52:31.515 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:31.519
2025-07-02 05:52:31.525 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:31.530 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:31.534
2025-07-02 05:52:31.539 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:31.544 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:31.549 alo = 447, ahi = 1101
2025-07-02 05:52:31.554 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:31.558 blo = 447, bhi = 1101
2025-07-02 05:52:31.563
2025-07-02 05:52:31.568 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:31.572 g = []
2025-07-02 05:52:31.577 if alo < ahi:
2025-07-02 05:52:31.581 if blo < bhi:
2025-07-02 05:52:31.586 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:31.591 else:
2025-07-02 05:52:31.595 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:31.600 elif blo < bhi:
2025-07-02 05:52:31.605 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:31.609
2025-07-02 05:52:31.614 >       yield from g
2025-07-02 05:52:31.619
2025-07-02 05:52:31.624 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:31.629 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:31.634
2025-07-02 05:52:31.639 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:31.644 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:31.649 alo = 447, ahi = 1101
2025-07-02 05:52:31.654 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:31.660 blo = 447, bhi = 1101
2025-07-02 05:52:31.670
2025-07-02 05:52:31.681 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:31.690 r"""
2025-07-02 05:52:31.701 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:31.711 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:31.722 synch point, and intraline difference marking is done on the
2025-07-02 05:52:31.732 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:31.742
2025-07-02 05:52:31.758 Example:
2025-07-02 05:52:31.764
2025-07-02 05:52:31.772 >>> d = Differ()
2025-07-02 05:52:31.777 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:31.782 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:31.787 >>> print(''.join(results), end="")
2025-07-02 05:52:31.792 - abcDefghiJkl
2025-07-02 05:52:31.803 + abcdefGhijkl
2025-07-02 05:52:31.814 """
2025-07-02 05:52:31.820
2025-07-02 05:52:31.826 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:31.832 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:31.838 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:31.849 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:31.861 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:31.874
2025-07-02 05:52:31.886 # search for the pair that matches best without being identical
2025-07-02 05:52:31.899 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:31.911 # on junk -- unless we have to)
2025-07-02 05:52:31.920 for j in range(blo, bhi):
2025-07-02 05:52:31.930 bj = b[j]
2025-07-02 05:52:31.942 cruncher.set_seq2(bj)
2025-07-02 05:52:31.953 for i in range(alo, ahi):
2025-07-02 05:52:31.964 ai = a[i]
2025-07-02 05:52:31.977 if ai == bj:
2025-07-02 05:52:31.986 if eqi is None:
2025-07-02 05:52:31.994 eqi, eqj = i, j
2025-07-02 05:52:32.001 continue
2025-07-02 05:52:32.009 cruncher.set_seq1(ai)
2025-07-02 05:52:32.023 # computing similarity is expensive, so use the quick
2025-07-02 05:52:32.031 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:32.037 # compares by a factor of 3.
2025-07-02 05:52:32.043 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:32.050 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:32.058 # of the computation is cached by cruncher
2025-07-02 05:52:32.066 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:32.073 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:32.080 cruncher.ratio() > best_ratio:
2025-07-02 05:52:32.088 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:32.096 if best_ratio < cutoff:
2025-07-02 05:52:32.103 # no non-identical "pretty close" pair
2025-07-02 05:52:32.111 if eqi is None:
2025-07-02 05:52:32.119 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:32.127 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:32.134 return
2025-07-02 05:52:32.141 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:32.149 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:32.156 else:
2025-07-02 05:52:32.164 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:32.171 eqi = None
2025-07-02 05:52:32.178
2025-07-02 05:52:32.184 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:32.191 # identical
2025-07-02 05:52:32.198
2025-07-02 05:52:32.205 # pump out diffs from before the synch point
2025-07-02 05:52:32.211 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:32.218
2025-07-02 05:52:32.230 # do intraline marking on the synch pair
2025-07-02 05:52:32.239 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:32.247 if eqi is None:
2025-07-02 05:52:32.259 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:32.266 atags = btags = ""
2025-07-02 05:52:32.272 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:32.278 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:32.283 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:32.288 if tag == 'replace':
2025-07-02 05:52:32.293 atags += '^' * la
2025-07-02 05:52:32.299 btags += '^' * lb
2025-07-02 05:52:32.308 elif tag == 'delete':
2025-07-02 05:52:32.314 atags += '-' * la
2025-07-02 05:52:32.327 elif tag == 'insert':
2025-07-02 05:52:32.341 btags += '+' * lb
2025-07-02 05:52:32.355 elif tag == 'equal':
2025-07-02 05:52:32.365 atags += ' ' * la
2025-07-02 05:52:32.378 btags += ' ' * lb
2025-07-02 05:52:32.386 else:
2025-07-02 05:52:32.393 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:32.400 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:32.406 else:
2025-07-02 05:52:32.411 # the synch pair is identical
2025-07-02 05:52:32.416 yield '  ' + aelt
2025-07-02 05:52:32.421
2025-07-02 05:52:32.425 # pump out diffs from after the synch point
2025-07-02 05:52:32.430 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:32.435
2025-07-02 05:52:32.441 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:32.447 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:32.454
2025-07-02 05:52:32.462 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:32.471 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:32.478 alo = 448, ahi = 1101
2025-07-02 05:52:32.487 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:32.498 blo = 448, bhi = 1101
2025-07-02 05:52:32.505
2025-07-02 05:52:32.513 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:32.520 g = []
2025-07-02 05:52:32.526 if alo < ahi:
2025-07-02 05:52:32.531 if blo < bhi:
2025-07-02 05:52:32.536 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:32.541 else:
2025-07-02 05:52:32.545 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:32.551 elif blo < bhi:
2025-07-02 05:52:32.559 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:32.570
2025-07-02 05:52:32.579 >       yield from g
2025-07-02 05:52:32.586
2025-07-02 05:52:32.592 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:32.599 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:32.605
2025-07-02 05:52:32.611 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:32.619 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:32.626 alo = 448, ahi = 1101
2025-07-02 05:52:32.633 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:32.639 blo = 448, bhi = 1101
2025-07-02 05:52:32.645
2025-07-02 05:52:32.651 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:32.658 r"""
2025-07-02 05:52:32.666 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:32.678 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:32.687 synch point, and intraline difference marking is done on the
2025-07-02 05:52:32.694 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:32.701
2025-07-02 05:52:32.707 Example:
2025-07-02 05:52:32.713
2025-07-02 05:52:32.719 >>> d = Differ()
2025-07-02 05:52:32.726 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:32.732 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:32.738 >>> print(''.join(results), end="")
2025-07-02 05:52:32.743 - abcDefghiJkl
2025-07-02 05:52:32.767 + abcdefGhijkl
2025-07-02 05:52:32.790 """
2025-07-02 05:52:32.800
2025-07-02 05:52:32.808 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:32.818 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:32.832 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:32.842 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:32.851 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:32.860
2025-07-02 05:52:32.871 # search for the pair that matches best without being identical
2025-07-02 05:52:32.879 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:32.888 # on junk -- unless we have to)
2025-07-02 05:52:32.895 for j in range(blo, bhi):
2025-07-02 05:52:32.901 bj = b[j]
2025-07-02 05:52:32.906 cruncher.set_seq2(bj)
2025-07-02 05:52:32.913 for i in range(alo, ahi):
2025-07-02 05:52:32.919 ai = a[i]
2025-07-02 05:52:32.924 if ai == bj:
2025-07-02 05:52:32.929 if eqi is None:
2025-07-02 05:52:32.935 eqi, eqj = i, j
2025-07-02 05:52:32.939 continue
2025-07-02 05:52:32.945 cruncher.set_seq1(ai)
2025-07-02 05:52:32.952 # computing similarity is expensive, so use the quick
2025-07-02 05:52:32.964 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:32.974 # compares by a factor of 3.
2025-07-02 05:52:32.984 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:32.992 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:32.999 # of the computation is cached by cruncher
2025-07-02 05:52:33.008 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:33.021 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:33.031 cruncher.ratio() > best_ratio:
2025-07-02 05:52:33.040 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:33.049 if best_ratio < cutoff:
2025-07-02 05:52:33.055 # no non-identical "pretty close" pair
2025-07-02 05:52:33.061 if eqi is None:
2025-07-02 05:52:33.069 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:33.076 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:33.082 return
2025-07-02 05:52:33.089 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:33.095 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:33.101 else:
2025-07-02 05:52:33.111 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:33.119 eqi = None
2025-07-02 05:52:33.128
2025-07-02 05:52:33.135 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:33.143 # identical
2025-07-02 05:52:33.155
2025-07-02 05:52:33.163 # pump out diffs from before the synch point
2025-07-02 05:52:33.170 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:33.177
2025-07-02 05:52:33.183 # do intraline marking on the synch pair
2025-07-02 05:52:33.189 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:33.194 if eqi is None:
2025-07-02 05:52:33.205 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:33.214 atags = btags = ""
2025-07-02 05:52:33.222 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:33.231 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:33.241 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:33.252 if tag == 'replace':
2025-07-02 05:52:33.264 atags += '^' * la
2025-07-02 05:52:33.273 btags += '^' * lb
2025-07-02 05:52:33.285 elif tag == 'delete':
2025-07-02 05:52:33.295 atags += '-' * la
2025-07-02 05:52:33.303 elif tag == 'insert':
2025-07-02 05:52:33.309 btags += '+' * lb
2025-07-02 05:52:33.315 elif tag == 'equal':
2025-07-02 05:52:33.321 atags += ' ' * la
2025-07-02 05:52:33.326 btags += ' ' * lb
2025-07-02 05:52:33.330 else:
2025-07-02 05:52:33.334 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:33.339 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:33.343 else:
2025-07-02 05:52:33.348 # the synch pair is identical
2025-07-02 05:52:33.352 yield '  ' + aelt
2025-07-02 05:52:33.356
2025-07-02 05:52:33.361 # pump out diffs from after the synch point
2025-07-02 05:52:33.366 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:33.370
2025-07-02 05:52:33.375 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:33.379 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:33.384
2025-07-02 05:52:33.389 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:33.398 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:33.410 alo = 449, ahi = 1101
2025-07-02 05:52:33.419 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:33.429 blo = 449, bhi = 1101
2025-07-02 05:52:33.437
2025-07-02 05:52:33.444 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:33.450 g = []
2025-07-02 05:52:33.455 if alo < ahi:
2025-07-02 05:52:33.459 if blo < bhi:
2025-07-02 05:52:33.464 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:33.469 else:
2025-07-02 05:52:33.481 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:33.490 elif blo < bhi:
2025-07-02 05:52:33.498 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:33.508
2025-07-02 05:52:33.518 >       yield from g
2025-07-02 05:52:33.528
2025-07-02 05:52:33.539 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:33.551 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:33.561
2025-07-02 05:52:33.569 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:33.578 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:33.584 alo = 449, ahi = 1101
2025-07-02 05:52:33.592 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:33.598 blo = 449, bhi = 1101
2025-07-02 05:52:33.604
2025-07-02 05:52:33.610 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:33.623 r"""
2025-07-02 05:52:33.632 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:33.638 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:33.645 synch point, and intraline difference marking is done on the
2025-07-02 05:52:33.651 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:33.657
2025-07-02 05:52:33.663 Example:
2025-07-02 05:52:33.675
2025-07-02 05:52:33.688 >>> d = Differ()
2025-07-02 05:52:33.695 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:33.701 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:33.708 >>> print(''.join(results), end="")
2025-07-02 05:52:33.714 - abcDefghiJkl
2025-07-02 05:52:33.726 + abcdefGhijkl
2025-07-02 05:52:33.738 """
2025-07-02 05:52:33.747
2025-07-02 05:52:33.757 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:33.765 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:33.777 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:33.789 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:33.798 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:33.809
2025-07-02 05:52:33.819 # search for the pair that matches best without being identical
2025-07-02 05:52:33.828 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:33.836 # on junk -- unless we have to)
2025-07-02 05:52:33.843 for j in range(blo, bhi):
2025-07-02 05:52:33.849 bj = b[j]
2025-07-02 05:52:33.855 cruncher.set_seq2(bj)
2025-07-02 05:52:33.861 for i in range(alo, ahi):
2025-07-02 05:52:33.867 ai = a[i]
2025-07-02 05:52:33.872 if ai == bj:
2025-07-02 05:52:33.878 if eqi is None:
2025-07-02 05:52:33.884 eqi, eqj = i, j
2025-07-02 05:52:33.891 continue
2025-07-02 05:52:33.897 cruncher.set_seq1(ai)
2025-07-02 05:52:33.905 # computing similarity is expensive, so use the quick
2025-07-02 05:52:33.913 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:33.920 # compares by a factor of 3.
2025-07-02 05:52:33.928 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:33.936 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:33.944 # of the computation is cached by cruncher
2025-07-02 05:52:33.951 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:33.959 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:33.966 cruncher.ratio() > best_ratio:
2025-07-02 05:52:33.975 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:33.984 if best_ratio < cutoff:
2025-07-02 05:52:33.992 # no non-identical "pretty close" pair
2025-07-02 05:52:34.000 if eqi is None:
2025-07-02 05:52:34.007 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:34.015 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:34.026 return
2025-07-02 05:52:34.035 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:34.043 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:34.053 else:
2025-07-02 05:52:34.061 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:34.075 eqi = None
2025-07-02 05:52:34.084
2025-07-02 05:52:34.097 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:34.107 # identical
2025-07-02 05:52:34.116
2025-07-02 05:52:34.130 # pump out diffs from before the synch point
2025-07-02 05:52:34.140 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:34.146
2025-07-02 05:52:34.153 # do intraline marking on the synch pair
2025-07-02 05:52:34.164 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:34.173 if eqi is None:
2025-07-02 05:52:34.181 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:34.187 atags = btags = ""
2025-07-02 05:52:34.195 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:34.209 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:34.220 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:34.232 if tag == 'replace':
2025-07-02 05:52:34.242 atags += '^' * la
2025-07-02 05:52:34.253 btags += '^' * lb
2025-07-02 05:52:34.263 elif tag == 'delete':
2025-07-02 05:52:34.272 atags += '-' * la
2025-07-02 05:52:34.279 elif tag == 'insert':
2025-07-02 05:52:34.288 btags += '+' * lb
2025-07-02 05:52:34.300 elif tag == 'equal':
2025-07-02 05:52:34.308 atags += ' ' * la
2025-07-02 05:52:34.315 btags += ' ' * lb
2025-07-02 05:52:34.328 else:
2025-07-02 05:52:34.339 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:34.346 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:34.352 else:
2025-07-02 05:52:34.358 # the synch pair is identical
2025-07-02 05:52:34.365 yield '  ' + aelt
2025-07-02 05:52:34.371
2025-07-02 05:52:34.378 # pump out diffs from after the synch point
2025-07-02 05:52:34.390 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:34.398
2025-07-02 05:52:34.406 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:34.416 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:34.425
2025-07-02 05:52:34.433 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:34.447 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:34.458 alo = 450, ahi = 1101
2025-07-02 05:52:34.466 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:34.477 blo = 450, bhi = 1101
2025-07-02 05:52:34.488
2025-07-02 05:52:34.496 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:34.503 g = []
2025-07-02 05:52:34.510 if alo < ahi:
2025-07-02 05:52:34.521 if blo < bhi:
2025-07-02 05:52:34.531 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:34.538 else:
2025-07-02 05:52:34.547 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:34.555 elif blo < bhi:
2025-07-02 05:52:34.565 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:34.574
2025-07-02 05:52:34.581 >       yield from g
2025-07-02 05:52:34.588
2025-07-02 05:52:34.595 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:34.602 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:34.608
2025-07-02 05:52:34.615 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:34.622 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:34.628 alo = 450, ahi = 1101
2025-07-02 05:52:34.636 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:34.643 blo = 450, bhi = 1101
2025-07-02 05:52:34.650
2025-07-02 05:52:34.663 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:34.671 r"""
2025-07-02 05:52:34.679 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:34.685 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:34.691 synch point, and intraline difference marking is done on the
2025-07-02 05:52:34.698 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:34.709
2025-07-02 05:52:34.719 Example:
2025-07-02 05:52:34.727
2025-07-02 05:52:34.734 >>> d = Differ()
2025-07-02 05:52:34.742 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:34.748 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:34.754 >>> print(''.join(results), end="")
2025-07-02 05:52:34.761 - abcDefghiJkl
2025-07-02 05:52:34.773 + abcdefGhijkl
2025-07-02 05:52:34.783 """
2025-07-02 05:52:34.789
2025-07-02 05:52:34.795 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:34.801 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:34.808 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:34.815 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:34.823 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:34.832
2025-07-02 05:52:34.843 # search for the pair that matches best without being identical
2025-07-02 05:52:34.851 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:34.859 # on junk -- unless we have to)
2025-07-02 05:52:34.866 for j in range(blo, bhi):
2025-07-02 05:52:34.877 bj = b[j]
2025-07-02 05:52:34.887 cruncher.set_seq2(bj)
2025-07-02 05:52:34.895 for i in range(alo, ahi):
2025-07-02 05:52:34.901 ai = a[i]
2025-07-02 05:52:34.907 if ai == bj:
2025-07-02 05:52:34.914 if eqi is None:
2025-07-02 05:52:34.921 eqi, eqj = i, j
2025-07-02 05:52:34.928 continue
2025-07-02 05:52:34.936 cruncher.set_seq1(ai)
2025-07-02 05:52:34.943 # computing similarity is expensive, so use the quick
2025-07-02 05:52:34.951 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:34.959 # compares by a factor of 3.
2025-07-02 05:52:34.967 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:34.974 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:34.984 # of the computation is cached by cruncher
2025-07-02 05:52:34.997 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:35.006 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:35.013 cruncher.ratio() > best_ratio:
2025-07-02 05:52:35.019 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:35.025 if best_ratio < cutoff:
2025-07-02 05:52:35.033 # no non-identical "pretty close" pair
2025-07-02 05:52:35.040 if eqi is None:
2025-07-02 05:52:35.048 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:35.056 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:35.063 return
2025-07-02 05:52:35.076 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:35.085 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:35.092 else:
2025-07-02 05:52:35.099 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:35.108 eqi = None
2025-07-02 05:52:35.116
2025-07-02 05:52:35.124 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:35.130 # identical
2025-07-02 05:52:35.139
2025-07-02 05:52:35.152 # pump out diffs from before the synch point
2025-07-02 05:52:35.162 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:35.172
2025-07-02 05:52:35.181 # do intraline marking on the synch pair
2025-07-02 05:52:35.189 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:35.202 if eqi is None:
2025-07-02 05:52:35.213 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:35.223 atags = btags = ""
2025-07-02 05:52:35.231 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:35.240 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:35.252 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:35.259 if tag == 'replace':
2025-07-02 05:52:35.266 atags += '^' * la
2025-07-02 05:52:35.275 btags += '^' * lb
2025-07-02 05:52:35.284 elif tag == 'delete':
2025-07-02 05:52:35.291 atags += '-' * la
2025-07-02 05:52:35.296 elif tag == 'insert':
2025-07-02 05:52:35.301 btags += '+' * lb
2025-07-02 05:52:35.307 elif tag == 'equal':
2025-07-02 05:52:35.312 atags += ' ' * la
2025-07-02 05:52:35.319 btags += ' ' * lb
2025-07-02 05:52:35.326 else:
2025-07-02 05:52:35.332 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:35.339 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:35.345 else:
2025-07-02 05:52:35.350 # the synch pair is identical
2025-07-02 05:52:35.356 yield '  ' + aelt
2025-07-02 05:52:35.361
2025-07-02 05:52:35.369 # pump out diffs from after the synch point
2025-07-02 05:52:35.375 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:35.381
2025-07-02 05:52:35.387 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:35.393 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:35.397
2025-07-02 05:52:35.403 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:35.411 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:35.418 alo = 451, ahi = 1101
2025-07-02 05:52:35.430 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:35.440 blo = 451, bhi = 1101
2025-07-02 05:52:35.447
2025-07-02 05:52:35.455 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:35.463 g = []
2025-07-02 05:52:35.473 if alo < ahi:
2025-07-02 05:52:35.481 if blo < bhi:
2025-07-02 05:52:35.488 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:35.502 else:
2025-07-02 05:52:35.514 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:35.524 elif blo < bhi:
2025-07-02 05:52:35.533 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:35.540
2025-07-02 05:52:35.547 >       yield from g
2025-07-02 05:52:35.558
2025-07-02 05:52:35.570 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:35.580 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:35.591
2025-07-02 05:52:35.602 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:35.612 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:35.620 alo = 451, ahi = 1101
2025-07-02 05:52:35.629 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:35.635 blo = 451, bhi = 1101
2025-07-02 05:52:35.643
2025-07-02 05:52:35.655 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:35.663 r"""
2025-07-02 05:52:35.670 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:35.678 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:35.685 synch point, and intraline difference marking is done on the
2025-07-02 05:52:35.692 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:35.698
2025-07-02 05:52:35.704 Example:
2025-07-02 05:52:35.710
2025-07-02 05:52:35.716 >>> d = Differ()
2025-07-02 05:52:35.722 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:35.729 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:35.735 >>> print(''.join(results), end="")
2025-07-02 05:52:35.741 - abcDefghiJkl
2025-07-02 05:52:35.754 + abcdefGhijkl
2025-07-02 05:52:35.775 """
2025-07-02 05:52:35.781
2025-07-02 05:52:35.787 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:35.792 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:35.797 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:35.802 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:35.806 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:35.811
2025-07-02 05:52:35.819 # search for the pair that matches best without being identical
2025-07-02 05:52:35.831 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:35.841 # on junk -- unless we have to)
2025-07-02 05:52:35.853 for j in range(blo, bhi):
2025-07-02 05:52:35.863 bj = b[j]
2025-07-02 05:52:35.871 cruncher.set_seq2(bj)
2025-07-02 05:52:35.878 for i in range(alo, ahi):
2025-07-02 05:52:35.882 ai = a[i]
2025-07-02 05:52:35.887 if ai == bj:
2025-07-02 05:52:35.892 if eqi is None:
2025-07-02 05:52:35.899 eqi, eqj = i, j
2025-07-02 05:52:35.904 continue
2025-07-02 05:52:35.910 cruncher.set_seq1(ai)
2025-07-02 05:52:35.917 # computing similarity is expensive, so use the quick
2025-07-02 05:52:35.923 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:35.930 # compares by a factor of 3.
2025-07-02 05:52:35.941 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:35.949 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:35.956 # of the computation is cached by cruncher
2025-07-02 05:52:35.962 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:35.969 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:35.983 cruncher.ratio() > best_ratio:
2025-07-02 05:52:35.995 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:36.007 if best_ratio < cutoff:
2025-07-02 05:52:36.018 # no non-identical "pretty close" pair
2025-07-02 05:52:36.032 if eqi is None:
2025-07-02 05:52:36.045 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:36.055 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:36.063 return
2025-07-02 05:52:36.071 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:36.079 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:36.090 else:
2025-07-02 05:52:36.100 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:36.107 eqi = None
2025-07-02 05:52:36.118
2025-07-02 05:52:36.129 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:36.142 # identical
2025-07-02 05:52:36.154
2025-07-02 05:52:36.165 # pump out diffs from before the synch point
2025-07-02 05:52:36.177 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:36.189
2025-07-02 05:52:36.196 # do intraline marking on the synch pair
2025-07-02 05:52:36.209 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:36.219 if eqi is None:
2025-07-02 05:52:36.229 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:36.237 atags = btags = ""
2025-07-02 05:52:36.244 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:36.250 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:36.256 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:36.263 if tag == 'replace':
2025-07-02 05:52:36.271 atags += '^' * la
2025-07-02 05:52:36.283 btags += '^' * lb
2025-07-02 05:52:36.294 elif tag == 'delete':
2025-07-02 05:52:36.305 atags += '-' * la
2025-07-02 05:52:36.317 elif tag == 'insert':
2025-07-02 05:52:36.329 btags += '+' * lb
2025-07-02 05:52:36.337 elif tag == 'equal':
2025-07-02 05:52:36.345 atags += ' ' * la
2025-07-02 05:52:36.351 btags += ' ' * lb
2025-07-02 05:52:36.358 else:
2025-07-02 05:52:36.365 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:36.379 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:36.390 else:
2025-07-02 05:52:36.400 # the synch pair is identical
2025-07-02 05:52:36.407 yield '  ' + aelt
2025-07-02 05:52:36.414
2025-07-02 05:52:36.421 # pump out diffs from after the synch point
2025-07-02 05:52:36.427 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:36.433
2025-07-02 05:52:36.440 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:36.446 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:36.453
2025-07-02 05:52:36.459 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:36.465 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:36.470 alo = 452, ahi = 1101
2025-07-02 05:52:36.477 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:36.483 blo = 452, bhi = 1101
2025-07-02 05:52:36.489
2025-07-02 05:52:36.498 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:36.510 g = []
2025-07-02 05:52:36.519 if alo < ahi:
2025-07-02 05:52:36.531 if blo < bhi:
2025-07-02 05:52:36.542 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:36.549 else:
2025-07-02 05:52:36.557 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:36.565 elif blo < bhi:
2025-07-02 05:52:36.571 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:36.579
2025-07-02 05:52:36.590 >       yield from g
2025-07-02 05:52:36.599
2025-07-02 05:52:36.607 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:36.613 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:36.620
2025-07-02 05:52:36.627 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:36.635 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:36.646 alo = 452, ahi = 1101
2025-07-02 05:52:36.662 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:36.669 blo = 452, bhi = 1101
2025-07-02 05:52:36.674
2025-07-02 05:52:36.679 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:36.684 r"""
2025-07-02 05:52:36.693 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:36.707 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:36.716 synch point, and intraline difference marking is done on the
2025-07-02 05:52:36.724 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:36.731
2025-07-02 05:52:36.739 Example:
2025-07-02 05:52:36.746
2025-07-02 05:52:36.757 >>> d = Differ()
2025-07-02 05:52:36.770 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:36.782 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:36.794 >>> print(''.join(results), end="")
2025-07-02 05:52:36.804 - abcDefghiJkl
2025-07-02 05:52:36.824 + abcdefGhijkl
2025-07-02 05:52:36.839 """
2025-07-02 05:52:36.847
2025-07-02 05:52:36.853 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:36.865 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:36.875 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:36.882 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:36.890 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:36.900
2025-07-02 05:52:36.910 # search for the pair that matches best without being identical
2025-07-02 05:52:36.919 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:36.927 # on junk -- unless we have to)
2025-07-02 05:52:36.934 for j in range(blo, bhi):
2025-07-02 05:52:36.942 bj = b[j]
2025-07-02 05:52:36.951 cruncher.set_seq2(bj)
2025-07-02 05:52:36.960 for i in range(alo, ahi):
2025-07-02 05:52:36.967 ai = a[i]
2025-07-02 05:52:36.974 if ai == bj:
2025-07-02 05:52:36.981 if eqi is None:
2025-07-02 05:52:36.987 eqi, eqj = i, j
2025-07-02 05:52:36.994 continue
2025-07-02 05:52:37.000 cruncher.set_seq1(ai)
2025-07-02 05:52:37.007 # computing similarity is expensive, so use the quick
2025-07-02 05:52:37.014 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:37.020 # compares by a factor of 3.
2025-07-02 05:52:37.027 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:37.034 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:37.040 # of the computation is cached by cruncher
2025-07-02 05:52:37.047 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:37.053 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:37.060 cruncher.ratio() > best_ratio:
2025-07-02 05:52:37.066 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:37.073 if best_ratio < cutoff:
2025-07-02 05:52:37.080 # no non-identical "pretty close" pair
2025-07-02 05:52:37.086 if eqi is None:
2025-07-02 05:52:37.091 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:37.097 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:37.104 return
2025-07-02 05:52:37.111 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:37.123 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:37.134 else:
2025-07-02 05:52:37.148 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:37.162 eqi = None
2025-07-02 05:52:37.171
2025-07-02 05:52:37.178 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:37.188 # identical
2025-07-02 05:52:37.197
2025-07-02 05:52:37.210 # pump out diffs from before the synch point
2025-07-02 05:52:37.221 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:37.228
2025-07-02 05:52:37.236 # do intraline marking on the synch pair
2025-07-02 05:52:37.243 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:37.254 if eqi is None:
2025-07-02 05:52:37.269 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:37.280 atags = btags = ""
2025-07-02 05:52:37.295 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:37.304 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:37.312 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:37.319 if tag == 'replace':
2025-07-02 05:52:37.325 atags += '^' * la
2025-07-02 05:52:37.331 btags += '^' * lb
2025-07-02 05:52:37.341 elif tag == 'delete':
2025-07-02 05:52:37.350 atags += '-' * la
2025-07-02 05:52:37.356 elif tag == 'insert':
2025-07-02 05:52:37.363 btags += '+' * lb
2025-07-02 05:52:37.368 elif tag == 'equal':
2025-07-02 05:52:37.381 atags += ' ' * la
2025-07-02 05:52:37.392 btags += ' ' * lb
2025-07-02 05:52:37.404 else:
2025-07-02 05:52:37.417 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:37.428 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:37.438 else:
2025-07-02 05:52:37.451 # the synch pair is identical
2025-07-02 05:52:37.464 yield '  ' + aelt
2025-07-02 05:52:37.473
2025-07-02 05:52:37.485 # pump out diffs from after the synch point
2025-07-02 05:52:37.500 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:37.512
2025-07-02 05:52:37.522 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:37.529 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:37.535
2025-07-02 05:52:37.541 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:37.547 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:37.554 alo = 453, ahi = 1101
2025-07-02 05:52:37.561 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:37.568 blo = 453, bhi = 1101
2025-07-02 05:52:37.574
2025-07-02 05:52:37.581 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:37.586 g = []
2025-07-02 05:52:37.592 if alo < ahi:
2025-07-02 05:52:37.598 if blo < bhi:
2025-07-02 05:52:37.605 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:37.611 else:
2025-07-02 05:52:37.623 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:37.635 elif blo < bhi:
2025-07-02 05:52:37.647 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:37.656
2025-07-02 05:52:37.666 >       yield from g
2025-07-02 05:52:37.675
2025-07-02 05:52:37.683 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:37.691 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:37.698
2025-07-02 05:52:37.709 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:37.719 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:37.727 alo = 453, ahi = 1101
2025-07-02 05:52:37.733 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:37.739 blo = 453, bhi = 1101
2025-07-02 05:52:37.745
2025-07-02 05:52:37.758 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:37.768 r"""
2025-07-02 05:52:37.776 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:37.783 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:37.790 synch point, and intraline difference marking is done on the
2025-07-02 05:52:37.796 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:37.802
2025-07-02 05:52:37.808 Example:
2025-07-02 05:52:37.814
2025-07-02 05:52:37.820 >>> d = Differ()
2025-07-02 05:52:37.826 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:37.833 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:37.838 >>> print(''.join(results), end="")
2025-07-02 05:52:37.847 - abcDefghiJkl
2025-07-02 05:52:37.866 + abcdefGhijkl
2025-07-02 05:52:37.882 """
2025-07-02 05:52:37.891
2025-07-02 05:52:37.899 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:37.906 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:37.918 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:37.928 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:37.936 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:37.942
2025-07-02 05:52:37.948 # search for the pair that matches best without being identical
2025-07-02 05:52:37.953 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:37.958 # on junk -- unless we have to)
2025-07-02 05:52:37.967 for j in range(blo, bhi):
2025-07-02 05:52:37.978 bj = b[j]
2025-07-02 05:52:37.990 cruncher.set_seq2(bj)
2025-07-02 05:52:38.000 for i in range(alo, ahi):
2025-07-02 05:52:38.009 ai = a[i]
2025-07-02 05:52:38.015 if ai == bj:
2025-07-02 05:52:38.022 if eqi is None:
2025-07-02 05:52:38.028 eqi, eqj = i, j
2025-07-02 05:52:38.041 continue
2025-07-02 05:52:38.055 cruncher.set_seq1(ai)
2025-07-02 05:52:38.066 # computing similarity is expensive, so use the quick
2025-07-02 05:52:38.080 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:38.091 # compares by a factor of 3.
2025-07-02 05:52:38.099 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:38.110 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:38.119 # of the computation is cached by cruncher
2025-07-02 05:52:38.127 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:38.134 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:38.143 cruncher.ratio() > best_ratio:
2025-07-02 05:52:38.155 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:38.164 if best_ratio < cutoff:
2025-07-02 05:52:38.171 # no non-identical "pretty close" pair
2025-07-02 05:52:38.177 if eqi is None:
2025-07-02 05:52:38.183 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:38.191 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:38.200 return
2025-07-02 05:52:38.210 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:38.219 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:38.226 else:
2025-07-02 05:52:38.233 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:38.247 eqi = None
2025-07-02 05:52:38.254
2025-07-02 05:52:38.261 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:38.268 # identical
2025-07-02 05:52:38.276
2025-07-02 05:52:38.284 # pump out diffs from before the synch point
2025-07-02 05:52:38.292 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:38.299
2025-07-02 05:52:38.307 # do intraline marking on the synch pair
2025-07-02 05:52:38.314 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:38.323 if eqi is None:
2025-07-02 05:52:38.335 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:38.343 atags = btags = ""
2025-07-02 05:52:38.350 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:38.357 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:38.361 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:38.366 if tag == 'replace':
2025-07-02 05:52:38.371 atags += '^' * la
2025-07-02 05:52:38.375 btags += '^' * lb
2025-07-02 05:52:38.380 elif tag == 'delete':
2025-07-02 05:52:38.385 atags += '-' * la
2025-07-02 05:52:38.391 elif tag == 'insert':
2025-07-02 05:52:38.399 btags += '+' * lb
2025-07-02 05:52:38.406 elif tag == 'equal':
2025-07-02 05:52:38.416 atags += ' ' * la
2025-07-02 05:52:38.427 btags += ' ' * lb
2025-07-02 05:52:38.437 else:
2025-07-02 05:52:38.443 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:38.449 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:38.456 else:
2025-07-02 05:52:38.462 # the synch pair is identical
2025-07-02 05:52:38.468 yield '  ' + aelt
2025-07-02 05:52:38.475
2025-07-02 05:52:38.484 # pump out diffs from after the synch point
2025-07-02 05:52:38.493 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:38.500
2025-07-02 05:52:38.506 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:38.512 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:38.519
2025-07-02 05:52:38.527 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:38.539 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:38.547 alo = 454, ahi = 1101
2025-07-02 05:52:38.554 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:38.560 blo = 454, bhi = 1101
2025-07-02 05:52:38.565
2025-07-02 05:52:38.573 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:38.581 g = []
2025-07-02 05:52:38.587 if alo < ahi:
2025-07-02 05:52:38.593 if blo < bhi:
2025-07-02 05:52:38.602 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:38.611 else:
2025-07-02 05:52:38.618 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:38.625 elif blo < bhi:
2025-07-02 05:52:38.631 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:38.638
2025-07-02 05:52:38.648 >       yield from g
2025-07-02 05:52:38.656
2025-07-02 05:52:38.662 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:38.668 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:38.675
2025-07-02 05:52:38.681 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:38.691 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:38.699 alo = 454, ahi = 1101
2025-07-02 05:52:38.712 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:38.722 blo = 454, bhi = 1101
2025-07-02 05:52:38.729
2025-07-02 05:52:38.735 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:38.741 r"""
2025-07-02 05:52:38.746 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:38.751 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:38.756 synch point, and intraline difference marking is done on the
2025-07-02 05:52:38.763 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:38.769
2025-07-02 05:52:38.775 Example:
2025-07-02 05:52:38.782
2025-07-02 05:52:38.791 >>> d = Differ()
2025-07-02 05:52:38.798 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:38.805 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:38.812 >>> print(''.join(results), end="")
2025-07-02 05:52:38.819 - abcDefghiJkl
2025-07-02 05:52:38.836 + abcdefGhijkl
2025-07-02 05:52:38.849 """
2025-07-02 05:52:38.856
2025-07-02 05:52:38.863 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:38.870 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:38.881 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:38.891 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:38.899 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:38.906
2025-07-02 05:52:38.913 # search for the pair that matches best without being identical
2025-07-02 05:52:38.919 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:38.925 # on junk -- unless we have to)
2025-07-02 05:52:38.932 for j in range(blo, bhi):
2025-07-02 05:52:38.941 bj = b[j]
2025-07-02 05:52:38.948 cruncher.set_seq2(bj)
2025-07-02 05:52:38.955 for i in range(alo, ahi):
2025-07-02 05:52:38.961 ai = a[i]
2025-07-02 05:52:38.967 if ai == bj:
2025-07-02 05:52:38.975 if eqi is None:
2025-07-02 05:52:38.986 eqi, eqj = i, j
2025-07-02 05:52:38.996 continue
2025-07-02 05:52:39.004 cruncher.set_seq1(ai)
2025-07-02 05:52:39.011 # computing similarity is expensive, so use the quick
2025-07-02 05:52:39.020 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:39.032 # compares by a factor of 3.
2025-07-02 05:52:39.043 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:39.052 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:39.061 # of the computation is cached by cruncher
2025-07-02 05:52:39.068 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:39.074 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:39.085 cruncher.ratio() > best_ratio:
2025-07-02 05:52:39.095 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:39.103 if best_ratio < cutoff:
2025-07-02 05:52:39.110 # no non-identical "pretty close" pair
2025-07-02 05:52:39.115 if eqi is None:
2025-07-02 05:52:39.126 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:39.138 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:39.149 return
2025-07-02 05:52:39.161 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:39.171 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:39.177 else:
2025-07-02 05:52:39.183 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:39.192 eqi = None
2025-07-02 05:52:39.205
2025-07-02 05:52:39.213 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:39.228 # identical
2025-07-02 05:52:39.239
2025-07-02 05:52:39.250 # pump out diffs from before the synch point
2025-07-02 05:52:39.259 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:39.270
2025-07-02 05:52:39.278 # do intraline marking on the synch pair
2025-07-02 05:52:39.289 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:39.299 if eqi is None:
2025-07-02 05:52:39.307 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:39.315 atags = btags = ""
2025-07-02 05:52:39.323 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:39.330 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:39.337 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:39.344 if tag == 'replace':
2025-07-02 05:52:39.350 atags += '^' * la
2025-07-02 05:52:39.356 btags += '^' * lb
2025-07-02 05:52:39.362 elif tag == 'delete':
2025-07-02 05:52:39.369 atags += '-' * la
2025-07-02 05:52:39.375 elif tag == 'insert':
2025-07-02 05:52:39.383 btags += '+' * lb
2025-07-02 05:52:39.395 elif tag == 'equal':
2025-07-02 05:52:39.406 atags += ' ' * la
2025-07-02 05:52:39.413 btags += ' ' * lb
2025-07-02 05:52:39.422 else:
2025-07-02 05:52:39.432 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:39.444 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:39.455 else:
2025-07-02 05:52:39.465 # the synch pair is identical
2025-07-02 05:52:39.477 yield '  ' + aelt
2025-07-02 05:52:39.486
2025-07-02 05:52:39.497 # pump out diffs from after the synch point
2025-07-02 05:52:39.507 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:39.515
2025-07-02 05:52:39.527 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:39.537 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:39.545
2025-07-02 05:52:39.552 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:39.560 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:39.566 alo = 455, ahi = 1101
2025-07-02 05:52:39.575 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:39.585 blo = 455, bhi = 1101
2025-07-02 05:52:39.594
2025-07-02 05:52:39.601 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:39.607 g = []
2025-07-02 05:52:39.615 if alo < ahi:
2025-07-02 05:52:39.629 if blo < bhi:
2025-07-02 05:52:39.639 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:39.647 else:
2025-07-02 05:52:39.661 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:39.672 elif blo < bhi:
2025-07-02 05:52:39.679 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:39.687
2025-07-02 05:52:39.699 >       yield from g
2025-07-02 05:52:39.711
2025-07-02 05:52:39.721 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:39.729 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:39.736
2025-07-02 05:52:39.743 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:39.752 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:39.764 alo = 455, ahi = 1101
2025-07-02 05:52:39.775 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:39.785 blo = 455, bhi = 1101
2025-07-02 05:52:39.792
2025-07-02 05:52:39.797 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:39.801 r"""
2025-07-02 05:52:39.806 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:39.811 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:39.817 synch point, and intraline difference marking is done on the
2025-07-02 05:52:39.824 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:39.831
2025-07-02 05:52:39.837 Example:
2025-07-02 05:52:39.842
2025-07-02 05:52:39.849 >>> d = Differ()
2025-07-02 05:52:39.855 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:39.861 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:39.867 >>> print(''.join(results), end="")
2025-07-02 05:52:39.873 - abcDefghiJkl
2025-07-02 05:52:39.888 + abcdefGhijkl
2025-07-02 05:52:39.900 """
2025-07-02 05:52:39.906
2025-07-02 05:52:39.913 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:39.919 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:39.925 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:39.932 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:39.938 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:39.944
2025-07-02 05:52:39.950 # search for the pair that matches best without being identical
2025-07-02 05:52:39.956 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:39.962 # on junk -- unless we have to)
2025-07-02 05:52:39.968 for j in range(blo, bhi):
2025-07-02 05:52:39.976 bj = b[j]
2025-07-02 05:52:39.985 cruncher.set_seq2(bj)
2025-07-02 05:52:39.992 for i in range(alo, ahi):
2025-07-02 05:52:39.998 ai = a[i]
2025-07-02 05:52:40.010 if ai == bj:
2025-07-02 05:52:40.018 if eqi is None:
2025-07-02 05:52:40.023 eqi, eqj = i, j
2025-07-02 05:52:40.029 continue
2025-07-02 05:52:40.035 cruncher.set_seq1(ai)
2025-07-02 05:52:40.049 # computing similarity is expensive, so use the quick
2025-07-02 05:52:40.060 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:40.068 # compares by a factor of 3.
2025-07-02 05:52:40.076 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:40.083 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:40.091 # of the computation is cached by cruncher
2025-07-02 05:52:40.102 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:40.111 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:40.119 cruncher.ratio() > best_ratio:
2025-07-02 05:52:40.127 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:40.134 if best_ratio < cutoff:
2025-07-02 05:52:40.140 # no non-identical "pretty close" pair
2025-07-02 05:52:40.147 if eqi is None:
2025-07-02 05:52:40.154 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:40.167 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:40.176 return
2025-07-02 05:52:40.183 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:40.190 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:40.204 else:
2025-07-02 05:52:40.213 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:40.221 eqi = None
2025-07-02 05:52:40.227
2025-07-02 05:52:40.235 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:40.249 # identical
2025-07-02 05:52:40.260
2025-07-02 05:52:40.269 # pump out diffs from before the synch point
2025-07-02 05:52:40.276 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:40.282
2025-07-02 05:52:40.288 # do intraline marking on the synch pair
2025-07-02 05:52:40.297 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:40.309 if eqi is None:
2025-07-02 05:52:40.320 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:40.328 atags = btags = ""
2025-07-02 05:52:40.334 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:40.340 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:40.346 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:40.351 if tag == 'replace':
2025-07-02 05:52:40.356 atags += '^' * la
2025-07-02 05:52:40.363 btags += '^' * lb
2025-07-02 05:52:40.371 elif tag == 'delete':
2025-07-02 05:52:40.378 atags += '-' * la
2025-07-02 05:52:40.386 elif tag == 'insert':
2025-07-02 05:52:40.395 btags += '+' * lb
2025-07-02 05:52:40.403 elif tag == 'equal':
2025-07-02 05:52:40.410 atags += ' ' * la
2025-07-02 05:52:40.417 btags += ' ' * lb
2025-07-02 05:52:40.424 else:
2025-07-02 05:52:40.430 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:40.439 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:40.451 else:
2025-07-02 05:52:40.460 # the synch pair is identical
2025-07-02 05:52:40.469 yield '  ' + aelt
2025-07-02 05:52:40.482
2025-07-02 05:52:40.494 # pump out diffs from after the synch point
2025-07-02 05:52:40.503 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:40.511
2025-07-02 05:52:40.518 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:40.529 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:40.537
2025-07-02 05:52:40.545 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:40.556 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:40.568 alo = 456, ahi = 1101
2025-07-02 05:52:40.580 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:40.589 blo = 456, bhi = 1101
2025-07-02 05:52:40.596
2025-07-02 05:52:40.602 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:40.613 g = []
2025-07-02 05:52:40.623 if alo < ahi:
2025-07-02 05:52:40.631 if blo < bhi:
2025-07-02 05:52:40.639 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:40.651 else:
2025-07-02 05:52:40.660 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:40.667 elif blo < bhi:
2025-07-02 05:52:40.673 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:40.679
2025-07-02 05:52:40.685 >       yield from g
2025-07-02 05:52:40.691
2025-07-02 05:52:40.697 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:40.704 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:40.711
2025-07-02 05:52:40.722 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:40.733 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:40.740 alo = 456, ahi = 1101
2025-07-02 05:52:40.748 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:40.754 blo = 456, bhi = 1101
2025-07-02 05:52:40.760
2025-07-02 05:52:40.767 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:40.773 r"""
2025-07-02 05:52:40.781 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:40.793 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:40.802 synch point, and intraline difference marking is done on the
2025-07-02 05:52:40.808 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:40.814
2025-07-02 05:52:40.820 Example:
2025-07-02 05:52:40.824
2025-07-02 05:52:40.829 >>> d = Differ()
2025-07-02 05:52:40.833 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:40.838 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:40.843 >>> print(''.join(results), end="")
2025-07-02 05:52:40.848 - abcDefghiJkl
2025-07-02 05:52:40.859 + abcdefGhijkl
2025-07-02 05:52:40.871 """
2025-07-02 05:52:40.877
2025-07-02 05:52:40.881 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:40.887 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:40.892 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:40.898 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:40.905 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:40.910
2025-07-02 05:52:40.915 # search for the pair that matches best without being identical
2025-07-02 05:52:40.923 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:40.934 # on junk -- unless we have to)
2025-07-02 05:52:40.943 for j in range(blo, bhi):
2025-07-02 05:52:40.952 bj = b[j]
2025-07-02 05:52:40.959 cruncher.set_seq2(bj)
2025-07-02 05:52:40.966 for i in range(alo, ahi):
2025-07-02 05:52:40.971 ai = a[i]
2025-07-02 05:52:40.978 if ai == bj:
2025-07-02 05:52:40.984 if eqi is None:
2025-07-02 05:52:40.991 eqi, eqj = i, j
2025-07-02 05:52:41.004 continue
2025-07-02 05:52:41.013 cruncher.set_seq1(ai)
2025-07-02 05:52:41.023 # computing similarity is expensive, so use the quick
2025-07-02 05:52:41.030 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:41.036 # compares by a factor of 3.
2025-07-02 05:52:41.042 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:41.048 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:41.055 # of the computation is cached by cruncher
2025-07-02 05:52:41.069 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:41.080 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:41.091 cruncher.ratio() > best_ratio:
2025-07-02 05:52:41.099 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:41.106 if best_ratio < cutoff:
2025-07-02 05:52:41.120 # no non-identical "pretty close" pair
2025-07-02 05:52:41.131 if eqi is None:
2025-07-02 05:52:41.141 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:41.155 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:41.165 return
2025-07-02 05:52:41.176 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:41.185 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:41.192 else:
2025-07-02 05:52:41.198 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:41.207 eqi = None
2025-07-02 05:52:41.220
2025-07-02 05:52:41.230 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:41.236 # identical
2025-07-02 05:52:41.242
2025-07-02 05:52:41.247 # pump out diffs from before the synch point
2025-07-02 05:52:41.252 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:41.258
2025-07-02 05:52:41.267 # do intraline marking on the synch pair
2025-07-02 05:52:41.275 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:41.282 if eqi is None:
2025-07-02 05:52:41.288 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:41.295 atags = btags = ""
2025-07-02 05:52:41.302 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:41.311 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:41.323 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:41.334 if tag == 'replace':
2025-07-02 05:52:41.342 atags += '^' * la
2025-07-02 05:52:41.350 btags += '^' * lb
2025-07-02 05:52:41.357 elif tag == 'delete':
2025-07-02 05:52:41.365 atags += '-' * la
2025-07-02 05:52:41.377 elif tag == 'insert':
2025-07-02 05:52:41.388 btags += '+' * lb
2025-07-02 05:52:41.397 elif tag == 'equal':
2025-07-02 05:52:41.404 atags += ' ' * la
2025-07-02 05:52:41.410 btags += ' ' * lb
2025-07-02 05:52:41.417 else:
2025-07-02 05:52:41.427 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:41.435 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:41.442 else:
2025-07-02 05:52:41.451 # the synch pair is identical
2025-07-02 05:52:41.462 yield '  ' + aelt
2025-07-02 05:52:41.468
2025-07-02 05:52:41.477 # pump out diffs from after the synch point
2025-07-02 05:52:41.486 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:41.498
2025-07-02 05:52:41.510 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:41.518 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:41.524
2025-07-02 05:52:41.533 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:41.539 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:41.544 alo = 457, ahi = 1101
2025-07-02 05:52:41.555 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:41.563 blo = 457, bhi = 1101
2025-07-02 05:52:41.569
2025-07-02 05:52:41.575 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:41.581 g = []
2025-07-02 05:52:41.587 if alo < ahi:
2025-07-02 05:52:41.593 if blo < bhi:
2025-07-02 05:52:41.598 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:41.605 else:
2025-07-02 05:52:41.612 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:41.618 elif blo < bhi:
2025-07-02 05:52:41.628 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:41.638
2025-07-02 05:52:41.646 >       yield from g
2025-07-02 05:52:41.653
2025-07-02 05:52:41.665 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:41.674 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:41.681
2025-07-02 05:52:41.687 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:41.693 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:41.699 alo = 457, ahi = 1101
2025-07-02 05:52:41.707 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:41.715 blo = 457, bhi = 1101
2025-07-02 05:52:41.727
2025-07-02 05:52:41.736 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:41.743 r"""
2025-07-02 05:52:41.755 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:41.764 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:41.771 synch point, and intraline difference marking is done on the
2025-07-02 05:52:41.783 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:41.793
2025-07-02 05:52:41.801 Example:
2025-07-02 05:52:41.815
2025-07-02 05:52:41.828 >>> d = Differ()
2025-07-02 05:52:41.837 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:41.845 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:41.852 >>> print(''.join(results), end="")
2025-07-02 05:52:41.859 - abcDefghiJkl
2025-07-02 05:52:41.879 + abcdefGhijkl
2025-07-02 05:52:41.900 """
2025-07-02 05:52:41.911
2025-07-02 05:52:41.921 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:41.931 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:41.940 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:41.948 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:41.955 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:41.962
2025-07-02 05:52:41.973 # search for the pair that matches best without being identical
2025-07-02 05:52:41.983 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:41.992 # on junk -- unless we have to)
2025-07-02 05:52:42.007 for j in range(blo, bhi):
2025-07-02 05:52:42.017 bj = b[j]
2025-07-02 05:52:42.025 cruncher.set_seq2(bj)
2025-07-02 05:52:42.032 for i in range(alo, ahi):
2025-07-02 05:52:42.040 ai = a[i]
2025-07-02 05:52:42.047 if ai == bj:
2025-07-02 05:52:42.055 if eqi is None:
2025-07-02 05:52:42.061 eqi, eqj = i, j
2025-07-02 05:52:42.067 continue
2025-07-02 05:52:42.073 cruncher.set_seq1(ai)
2025-07-02 05:52:42.079 # computing similarity is expensive, so use the quick
2025-07-02 05:52:42.085 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:42.093 # compares by a factor of 3.
2025-07-02 05:52:42.100 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:42.108 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:42.116 # of the computation is cached by cruncher
2025-07-02 05:52:42.123 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:42.128 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:42.135 cruncher.ratio() > best_ratio:
2025-07-02 05:52:42.141 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:42.147 if best_ratio < cutoff:
2025-07-02 05:52:42.153 # no non-identical "pretty close" pair
2025-07-02 05:52:42.159 if eqi is None:
2025-07-02 05:52:42.165 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:42.171 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:42.178 return
2025-07-02 05:52:42.190 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:42.199 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:42.206 else:
2025-07-02 05:52:42.212 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:42.219 eqi = None
2025-07-02 05:52:42.228
2025-07-02 05:52:42.236 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:42.243 # identical
2025-07-02 05:52:42.250
2025-07-02 05:52:42.257 # pump out diffs from before the synch point
2025-07-02 05:52:42.262 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:42.267
2025-07-02 05:52:42.276 # do intraline marking on the synch pair
2025-07-02 05:52:42.288 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:42.296 if eqi is None:
2025-07-02 05:52:42.303 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:42.309 atags = btags = ""
2025-07-02 05:52:42.315 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:42.320 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:42.326 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:42.332 if tag == 'replace':
2025-07-02 05:52:42.338 atags += '^' * la
2025-07-02 05:52:42.344 btags += '^' * lb
2025-07-02 05:52:42.354 elif tag == 'delete':
2025-07-02 05:52:42.362 atags += '-' * la
2025-07-02 05:52:42.370 elif tag == 'insert':
2025-07-02 05:52:42.377 btags += '+' * lb
2025-07-02 05:52:42.384 elif tag == 'equal':
2025-07-02 05:52:42.391 atags += ' ' * la
2025-07-02 05:52:42.398 btags += ' ' * lb
2025-07-02 05:52:42.406 else:
2025-07-02 05:52:42.413 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:42.425 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:42.434 else:
2025-07-02 05:52:42.440 # the synch pair is identical
2025-07-02 05:52:42.445 yield '  ' + aelt
2025-07-02 05:52:42.450
2025-07-02 05:52:42.460 # pump out diffs from after the synch point
2025-07-02 05:52:42.470 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:42.477
2025-07-02 05:52:42.483 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:42.489 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:42.494
2025-07-02 05:52:42.500 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:42.506 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:42.512 alo = 458, ahi = 1101
2025-07-02 05:52:42.520 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:42.527 blo = 458, bhi = 1101
2025-07-02 05:52:42.532
2025-07-02 05:52:42.537 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:42.547 g = []
2025-07-02 05:52:42.555 if alo < ahi:
2025-07-02 05:52:42.561 if blo < bhi:
2025-07-02 05:52:42.567 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:42.574 else:
2025-07-02 05:52:42.586 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:42.594 elif blo < bhi:
2025-07-02 05:52:42.602 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:42.609
2025-07-02 05:52:42.614 >       yield from g
2025-07-02 05:52:42.619
2025-07-02 05:52:42.625 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:42.633 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:42.640
2025-07-02 05:52:42.646 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:42.655 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:42.666 alo = 458, ahi = 1101
2025-07-02 05:52:42.677 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:42.685 blo = 458, bhi = 1101
2025-07-02 05:52:42.692
2025-07-02 05:52:42.699 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:42.704 r"""
2025-07-02 05:52:42.710 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:42.722 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:42.733 synch point, and intraline difference marking is done on the
2025-07-02 05:52:42.746 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:42.755
2025-07-02 05:52:42.764 Example:
2025-07-02 05:52:42.772
2025-07-02 05:52:42.779 >>> d = Differ()
2025-07-02 05:52:42.786 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:42.792 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:42.799 >>> print(''.join(results), end="")
2025-07-02 05:52:42.805 - abcDefghiJkl
2025-07-02 05:52:42.817 + abcdefGhijkl
2025-07-02 05:52:42.833 """
2025-07-02 05:52:42.845
2025-07-02 05:52:42.853 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:42.860 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:42.866 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:42.879 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:42.887 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:42.895
2025-07-02 05:52:42.901 # search for the pair that matches best without being identical
2025-07-02 05:52:42.906 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:42.916 # on junk -- unless we have to)
2025-07-02 05:52:42.927 for j in range(blo, bhi):
2025-07-02 05:52:42.936 bj = b[j]
2025-07-02 05:52:42.943 cruncher.set_seq2(bj)
2025-07-02 05:52:42.949 for i in range(alo, ahi):
2025-07-02 05:52:42.954 ai = a[i]
2025-07-02 05:52:42.959 if ai == bj:
2025-07-02 05:52:42.964 if eqi is None:
2025-07-02 05:52:42.970 eqi, eqj = i, j
2025-07-02 05:52:42.976 continue
2025-07-02 05:52:42.981 cruncher.set_seq1(ai)
2025-07-02 05:52:42.988 # computing similarity is expensive, so use the quick
2025-07-02 05:52:42.994 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:43.005 # compares by a factor of 3.
2025-07-02 05:52:43.016 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:43.024 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:43.031 # of the computation is cached by cruncher
2025-07-02 05:52:43.038 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:43.044 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:43.050 cruncher.ratio() > best_ratio:
2025-07-02 05:52:43.062 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:43.071 if best_ratio < cutoff:
2025-07-02 05:52:43.078 # no non-identical "pretty close" pair
2025-07-02 05:52:43.089 if eqi is None:
2025-07-02 05:52:43.099 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:43.106 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:43.116 return
2025-07-02 05:52:43.125 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:43.135 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:43.144 else:
2025-07-02 05:52:43.153 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:43.159 eqi = None
2025-07-02 05:52:43.165
2025-07-02 05:52:43.172 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:43.176 # identical
2025-07-02 05:52:43.182
2025-07-02 05:52:43.192 # pump out diffs from before the synch point
2025-07-02 05:52:43.201 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:43.210
2025-07-02 05:52:43.217 # do intraline marking on the synch pair
2025-07-02 05:52:43.224 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:43.230 if eqi is None:
2025-07-02 05:52:43.235 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:43.241 atags = btags = ""
2025-07-02 05:52:43.246 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:43.254 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:43.263 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:43.271 if tag == 'replace':
2025-07-02 05:52:43.283 atags += '^' * la
2025-07-02 05:52:43.295 btags += '^' * lb
2025-07-02 05:52:43.307 elif tag == 'delete':
2025-07-02 05:52:43.316 atags += '-' * la
2025-07-02 05:52:43.331 elif tag == 'insert':
2025-07-02 05:52:43.344 btags += '+' * lb
2025-07-02 05:52:43.355 elif tag == 'equal':
2025-07-02 05:52:43.364 atags += ' ' * la
2025-07-02 05:52:43.371 btags += ' ' * lb
2025-07-02 05:52:43.377 else:
2025-07-02 05:52:43.383 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:43.391 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:43.402 else:
2025-07-02 05:52:43.412 # the synch pair is identical
2025-07-02 05:52:43.420 yield '  ' + aelt
2025-07-02 05:52:43.427
2025-07-02 05:52:43.439 # pump out diffs from after the synch point
2025-07-02 05:52:43.451 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:43.461
2025-07-02 05:52:43.471 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:43.484 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:43.495
2025-07-02 05:52:43.504 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:43.517 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:43.527 alo = 459, ahi = 1101
2025-07-02 05:52:43.536 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:43.544 blo = 459, bhi = 1101
2025-07-02 05:52:43.550
2025-07-02 05:52:43.556 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:43.563 g = []
2025-07-02 05:52:43.574 if alo < ahi:
2025-07-02 05:52:43.582 if blo < bhi:
2025-07-02 05:52:43.591 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:43.604 else:
2025-07-02 05:52:43.613 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:43.623 elif blo < bhi:
2025-07-02 05:52:43.633 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:43.642
2025-07-02 05:52:43.654 >       yield from g
2025-07-02 05:52:43.667
2025-07-02 05:52:43.680 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:43.691 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:43.698
2025-07-02 05:52:43.706 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:43.713 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:43.719 alo = 459, ahi = 1101
2025-07-02 05:52:43.726 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:43.735 blo = 459, bhi = 1101
2025-07-02 05:52:43.748
2025-07-02 05:52:43.757 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:43.763 r"""
2025-07-02 05:52:43.770 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:43.777 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:43.783 synch point, and intraline difference marking is done on the
2025-07-02 05:52:43.792 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:43.805
2025-07-02 05:52:43.815 Example:
2025-07-02 05:52:43.824
2025-07-02 05:52:43.831 >>> d = Differ()
2025-07-02 05:52:43.838 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:43.848 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:43.861 >>> print(''.join(results), end="")
2025-07-02 05:52:43.871 - abcDefghiJkl
2025-07-02 05:52:43.889 + abcdefGhijkl
2025-07-02 05:52:43.911 """
2025-07-02 05:52:43.923
2025-07-02 05:52:43.935 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:43.946 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:43.959 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:43.968 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:43.976 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:43.983
2025-07-02 05:52:43.989 # search for the pair that matches best without being identical
2025-07-02 05:52:43.999 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:44.011 # on junk -- unless we have to)
2025-07-02 05:52:44.022 for j in range(blo, bhi):
2025-07-02 05:52:44.032 bj = b[j]
2025-07-02 05:52:44.043 cruncher.set_seq2(bj)
2025-07-02 05:52:44.052 for i in range(alo, ahi):
2025-07-02 05:52:44.059 ai = a[i]
2025-07-02 05:52:44.066 if ai == bj:
2025-07-02 05:52:44.075 if eqi is None:
2025-07-02 05:52:44.087 eqi, eqj = i, j
2025-07-02 05:52:44.096 continue
2025-07-02 05:52:44.103 cruncher.set_seq1(ai)
2025-07-02 05:52:44.110 # computing similarity is expensive, so use the quick
2025-07-02 05:52:44.116 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:44.123 # compares by a factor of 3.
2025-07-02 05:52:44.131 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:44.141 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:44.150 # of the computation is cached by cruncher
2025-07-02 05:52:44.158 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:44.171 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:44.181 cruncher.ratio() > best_ratio:
2025-07-02 05:52:44.189 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:44.205 if best_ratio < cutoff:
2025-07-02 05:52:44.217 # no non-identical "pretty close" pair
2025-07-02 05:52:44.227 if eqi is None:
2025-07-02 05:52:44.237 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:44.250 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:44.261 return
2025-07-02 05:52:44.269 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:44.276 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:44.282 else:
2025-07-02 05:52:44.289 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:44.294 eqi = None
2025-07-02 05:52:44.299
2025-07-02 05:52:44.305 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:44.311 # identical
2025-07-02 05:52:44.320
2025-07-02 05:52:44.330 # pump out diffs from before the synch point
2025-07-02 05:52:44.339 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:44.347
2025-07-02 05:52:44.355 # do intraline marking on the synch pair
2025-07-02 05:52:44.362 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:44.373 if eqi is None:
2025-07-02 05:52:44.383 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:44.391 atags = btags = ""
2025-07-02 05:52:44.401 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:44.416 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:44.427 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:44.440 if tag == 'replace':
2025-07-02 05:52:44.451 atags += '^' * la
2025-07-02 05:52:44.462 btags += '^' * lb
2025-07-02 05:52:44.470 elif tag == 'delete':
2025-07-02 05:52:44.477 atags += '-' * la
2025-07-02 05:52:44.483 elif tag == 'insert':
2025-07-02 05:52:44.490 btags += '+' * lb
2025-07-02 05:52:44.497 elif tag == 'equal':
2025-07-02 05:52:44.504 atags += ' ' * la
2025-07-02 05:52:44.511 btags += ' ' * lb
2025-07-02 05:52:44.519 else:
2025-07-02 05:52:44.530 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:44.539 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:44.545 else:
2025-07-02 05:52:44.550 # the synch pair is identical
2025-07-02 05:52:44.556 yield '  ' + aelt
2025-07-02 05:52:44.562
2025-07-02 05:52:44.567 # pump out diffs from after the synch point
2025-07-02 05:52:44.573 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:44.578
2025-07-02 05:52:44.583 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:44.591 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:44.601
2025-07-02 05:52:44.610 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:44.623 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:44.633 alo = 460, ahi = 1101
2025-07-02 05:52:44.640 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:44.648 blo = 460, bhi = 1101
2025-07-02 05:52:44.657
2025-07-02 05:52:44.667 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:44.678 g = []
2025-07-02 05:52:44.690 if alo < ahi:
2025-07-02 05:52:44.702 if blo < bhi:
2025-07-02 05:52:44.711 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:44.719 else:
2025-07-02 05:52:44.726 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:44.737 elif blo < bhi:
2025-07-02 05:52:44.749 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:44.761
2025-07-02 05:52:44.770 >       yield from g
2025-07-02 05:52:44.779
2025-07-02 05:52:44.787 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:44.794 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:44.799
2025-07-02 05:52:44.804 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:44.815 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:44.825 alo = 460, ahi = 1101
2025-07-02 05:52:44.834 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:44.844 blo = 460, bhi = 1101
2025-07-02 05:52:44.855
2025-07-02 05:52:44.864 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:44.871 r"""
2025-07-02 05:52:44.878 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:44.885 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:44.895 synch point, and intraline difference marking is done on the
2025-07-02 05:52:44.907 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:44.915
2025-07-02 05:52:44.923 Example:
2025-07-02 05:52:44.930
2025-07-02 05:52:44.940 >>> d = Differ()
2025-07-02 05:52:44.951 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:44.959 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:44.967 >>> print(''.join(results), end="")
2025-07-02 05:52:44.974 - abcDefghiJkl
2025-07-02 05:52:44.986 + abcdefGhijkl
2025-07-02 05:52:44.998 """
2025-07-02 05:52:45.005
2025-07-02 05:52:45.012 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:45.018 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:45.025 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:45.033 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:45.045 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:45.054
2025-07-02 05:52:45.061 # search for the pair that matches best without being identical
2025-07-02 05:52:45.068 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:45.075 # on junk -- unless we have to)
2025-07-02 05:52:45.082 for j in range(blo, bhi):
2025-07-02 05:52:45.090 bj = b[j]
2025-07-02 05:52:45.097 cruncher.set_seq2(bj)
2025-07-02 05:52:45.104 for i in range(alo, ahi):
2025-07-02 05:52:45.111 ai = a[i]
2025-07-02 05:52:45.119 if ai == bj:
2025-07-02 05:52:45.127 if eqi is None:
2025-07-02 05:52:45.135 eqi, eqj = i, j
2025-07-02 05:52:45.143 continue
2025-07-02 05:52:45.151 cruncher.set_seq1(ai)
2025-07-02 05:52:45.159 # computing similarity is expensive, so use the quick
2025-07-02 05:52:45.166 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:45.176 # compares by a factor of 3.
2025-07-02 05:52:45.186 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:45.193 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:45.201 # of the computation is cached by cruncher
2025-07-02 05:52:45.208 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:45.215 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:45.221 cruncher.ratio() > best_ratio:
2025-07-02 05:52:45.226 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:45.231 if best_ratio < cutoff:
2025-07-02 05:52:45.236 # no non-identical "pretty close" pair
2025-07-02 05:52:45.241 if eqi is None:
2025-07-02 05:52:45.246 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:45.254 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:45.262 return
2025-07-02 05:52:45.271 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:45.279 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:45.286 else:
2025-07-02 05:52:45.292 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:45.297 eqi = None
2025-07-02 05:52:45.302
2025-07-02 05:52:45.306 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:45.312 # identical
2025-07-02 05:52:45.318
2025-07-02 05:52:45.324 # pump out diffs from before the synch point
2025-07-02 05:52:45.331 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:45.340
2025-07-02 05:52:45.347 # do intraline marking on the synch pair
2025-07-02 05:52:45.355 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:45.362 if eqi is None:
2025-07-02 05:52:45.370 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:45.376 atags = btags = ""
2025-07-02 05:52:45.383 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:45.388 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:45.394 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:45.400 if tag == 'replace':
2025-07-02 05:52:45.406 atags += '^' * la
2025-07-02 05:52:45.411 btags += '^' * lb
2025-07-02 05:52:45.418 elif tag == 'delete':
2025-07-02 05:52:45.425 atags += '-' * la
2025-07-02 05:52:45.432 elif tag == 'insert':
2025-07-02 05:52:45.439 btags += '+' * lb
2025-07-02 05:52:45.446 elif tag == 'equal':
2025-07-02 05:52:45.454 atags += ' ' * la
2025-07-02 05:52:45.464 btags += ' ' * lb
2025-07-02 05:52:45.474 else:
2025-07-02 05:52:45.482 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:45.489 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:45.495 else:
2025-07-02 05:52:45.503 # the synch pair is identical
2025-07-02 05:52:45.510 yield '  ' + aelt
2025-07-02 05:52:45.519
2025-07-02 05:52:45.531 # pump out diffs from after the synch point
2025-07-02 05:52:45.541 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:45.552
2025-07-02 05:52:45.564 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:45.573 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:45.585
2025-07-02 05:52:45.598 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:45.619 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:45.628 alo = 461, ahi = 1101
2025-07-02 05:52:45.641 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:45.651 blo = 461, bhi = 1101
2025-07-02 05:52:45.659
2025-07-02 05:52:45.667 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:45.673 g = []
2025-07-02 05:52:45.680 if alo < ahi:
2025-07-02 05:52:45.692 if blo < bhi:
2025-07-02 05:52:45.701 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:45.709 else:
2025-07-02 05:52:45.716 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:45.722 elif blo < bhi:
2025-07-02 05:52:45.727 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:45.731
2025-07-02 05:52:45.736 >       yield from g
2025-07-02 05:52:45.743
2025-07-02 05:52:45.750 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:45.759 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:45.768
2025-07-02 05:52:45.775 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:45.783 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:45.790 alo = 461, ahi = 1101
2025-07-02 05:52:45.798 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:45.804 blo = 461, bhi = 1101
2025-07-02 05:52:45.811
2025-07-02 05:52:45.819 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:45.828 r"""
2025-07-02 05:52:45.839 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:45.848 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:45.857 synch point, and intraline difference marking is done on the
2025-07-02 05:52:45.863 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:45.868
2025-07-02 05:52:45.872 Example:
2025-07-02 05:52:45.882
2025-07-02 05:52:45.888 >>> d = Differ()
2025-07-02 05:52:45.893 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:45.899 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:45.905 >>> print(''.join(results), end="")
2025-07-02 05:52:45.912 - abcDefghiJkl
2025-07-02 05:52:45.927 + abcdefGhijkl
2025-07-02 05:52:45.947 """
2025-07-02 05:52:45.960
2025-07-02 05:52:45.968 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:45.973 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:45.978 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:45.983 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:45.989 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:45.994
2025-07-02 05:52:45.999 # search for the pair that matches best without being identical
2025-07-02 05:52:46.005 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:46.012 # on junk -- unless we have to)
2025-07-02 05:52:46.019 for j in range(blo, bhi):
2025-07-02 05:52:46.027 bj = b[j]
2025-07-02 05:52:46.035 cruncher.set_seq2(bj)
2025-07-02 05:52:46.045 for i in range(alo, ahi):
2025-07-02 05:52:46.053 ai = a[i]
2025-07-02 05:52:46.061 if ai == bj:
2025-07-02 05:52:46.068 if eqi is None:
2025-07-02 05:52:46.076 eqi, eqj = i, j
2025-07-02 05:52:46.080 continue
2025-07-02 05:52:46.085 cruncher.set_seq1(ai)
2025-07-02 05:52:46.092 # computing similarity is expensive, so use the quick
2025-07-02 05:52:46.099 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:46.106 # compares by a factor of 3.
2025-07-02 05:52:46.115 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:46.127 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:46.135 # of the computation is cached by cruncher
2025-07-02 05:52:46.147 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:46.159 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:46.170 cruncher.ratio() > best_ratio:
2025-07-02 05:52:46.181 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:46.191 if best_ratio < cutoff:
2025-07-02 05:52:46.199 # no non-identical "pretty close" pair
2025-07-02 05:52:46.205 if eqi is None:
2025-07-02 05:52:46.211 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:46.216 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:46.222 return
2025-07-02 05:52:46.231 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:46.242 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:46.253 else:
2025-07-02 05:52:46.266 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:46.276 eqi = None
2025-07-02 05:52:46.284
2025-07-02 05:52:46.293 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:46.301 # identical
2025-07-02 05:52:46.307
2025-07-02 05:52:46.315 # pump out diffs from before the synch point
2025-07-02 05:52:46.325 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:46.334
2025-07-02 05:52:46.344 # do intraline marking on the synch pair
2025-07-02 05:52:46.356 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:46.368 if eqi is None:
2025-07-02 05:52:46.382 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:46.393 atags = btags = ""
2025-07-02 05:52:46.403 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:46.411 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:46.418 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:46.428 if tag == 'replace':
2025-07-02 05:52:46.436 atags += '^' * la
2025-07-02 05:52:46.443 btags += '^' * lb
2025-07-02 05:52:46.451 elif tag == 'delete':
2025-07-02 05:52:46.457 atags += '-' * la
2025-07-02 05:52:46.462 elif tag == 'insert':
2025-07-02 05:52:46.469 btags += '+' * lb
2025-07-02 05:52:46.474 elif tag == 'equal':
2025-07-02 05:52:46.479 atags += ' ' * la
2025-07-02 05:52:46.485 btags += ' ' * lb
2025-07-02 05:52:46.492 else:
2025-07-02 05:52:46.499 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:46.504 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:46.510 else:
2025-07-02 05:52:46.516 # the synch pair is identical
2025-07-02 05:52:46.524 yield '  ' + aelt
2025-07-02 05:52:46.533
2025-07-02 05:52:46.543 # pump out diffs from after the synch point
2025-07-02 05:52:46.555 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:46.563
2025-07-02 05:52:46.573 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:46.585 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:46.594
2025-07-02 05:52:46.602 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:46.609 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:46.616 alo = 462, ahi = 1101
2025-07-02 05:52:46.624 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:46.631 blo = 462, bhi = 1101
2025-07-02 05:52:46.643
2025-07-02 05:52:46.651 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:46.658 g = []
2025-07-02 05:52:46.664 if alo < ahi:
2025-07-02 05:52:46.670 if blo < bhi:
2025-07-02 05:52:46.676 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:46.682 else:
2025-07-02 05:52:46.690 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:46.698 elif blo < bhi:
2025-07-02 05:52:46.705 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:46.715
2025-07-02 05:52:46.722 >       yield from g
2025-07-02 05:52:46.728
2025-07-02 05:52:46.738 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:46.748 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:46.756
2025-07-02 05:52:46.763 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:46.772 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:46.778 alo = 462, ahi = 1101
2025-07-02 05:52:46.790 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:46.802 blo = 462, bhi = 1101
2025-07-02 05:52:46.812
2025-07-02 05:52:46.820 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:46.826 r"""
2025-07-02 05:52:46.833 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:46.842 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:46.853 synch point, and intraline difference marking is done on the
2025-07-02 05:52:46.866 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:46.877
2025-07-02 05:52:46.889 Example:
2025-07-02 05:52:46.902
2025-07-02 05:52:46.913 >>> d = Differ()
2025-07-02 05:52:46.921 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:46.929 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:46.935 >>> print(''.join(results), end="")
2025-07-02 05:52:46.942 - abcDefghiJkl
2025-07-02 05:52:46.953 + abcdefGhijkl
2025-07-02 05:52:46.965 """
2025-07-02 05:52:46.970
2025-07-02 05:52:46.976 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:46.982 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:46.989 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:46.997 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:47.012 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:47.021
2025-07-02 05:52:47.030 # search for the pair that matches best without being identical
2025-07-02 05:52:47.039 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:47.046 # on junk -- unless we have to)
2025-07-02 05:52:47.055 for j in range(blo, bhi):
2025-07-02 05:52:47.069 bj = b[j]
2025-07-02 05:52:47.080 cruncher.set_seq2(bj)
2025-07-02 05:52:47.088 for i in range(alo, ahi):
2025-07-02 05:52:47.095 ai = a[i]
2025-07-02 05:52:47.107 if ai == bj:
2025-07-02 05:52:47.114 if eqi is None:
2025-07-02 05:52:47.122 eqi, eqj = i, j
2025-07-02 05:52:47.131 continue
2025-07-02 05:52:47.143 cruncher.set_seq1(ai)
2025-07-02 05:52:47.155 # computing similarity is expensive, so use the quick
2025-07-02 05:52:47.167 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:47.179 # compares by a factor of 3.
2025-07-02 05:52:47.190 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:47.202 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:47.213 # of the computation is cached by cruncher
2025-07-02 05:52:47.227 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:47.237 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:47.245 cruncher.ratio() > best_ratio:
2025-07-02 05:52:47.253 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:47.260 if best_ratio < cutoff:
2025-07-02 05:52:47.274 # no non-identical "pretty close" pair
2025-07-02 05:52:47.287 if eqi is None:
2025-07-02 05:52:47.297 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:47.303 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:47.311 return
2025-07-02 05:52:47.321 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:47.329 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:47.336 else:
2025-07-02 05:52:47.342 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:47.353 eqi = None
2025-07-02 05:52:47.364
2025-07-02 05:52:47.373 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:47.379 # identical
2025-07-02 05:52:47.384
2025-07-02 05:52:47.389 # pump out diffs from before the synch point
2025-07-02 05:52:47.394 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:47.398
2025-07-02 05:52:47.406 # do intraline marking on the synch pair
2025-07-02 05:52:47.413 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:47.420 if eqi is None:
2025-07-02 05:52:47.428 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:47.435 atags = btags = ""
2025-07-02 05:52:47.444 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:47.455 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:47.461 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:47.473 if tag == 'replace':
2025-07-02 05:52:47.484 atags += '^' * la
2025-07-02 05:52:47.491 btags += '^' * lb
2025-07-02 05:52:47.498 elif tag == 'delete':
2025-07-02 05:52:47.506 atags += '-' * la
2025-07-02 05:52:47.515 elif tag == 'insert':
2025-07-02 05:52:47.524 btags += '+' * lb
2025-07-02 05:52:47.530 elif tag == 'equal':
2025-07-02 05:52:47.538 atags += ' ' * la
2025-07-02 05:52:47.548 btags += ' ' * lb
2025-07-02 05:52:47.555 else:
2025-07-02 05:52:47.566 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:47.577 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:47.585 else:
2025-07-02 05:52:47.599 # the synch pair is identical
2025-07-02 05:52:47.613 yield '  ' + aelt
2025-07-02 05:52:47.623
2025-07-02 05:52:47.631 # pump out diffs from after the synch point
2025-07-02 05:52:47.639 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:47.651
2025-07-02 05:52:47.662 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:47.674 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:47.684
2025-07-02 05:52:47.692 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:47.702 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:47.707 alo = 463, ahi = 1101
2025-07-02 05:52:47.721 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:47.730 blo = 463, bhi = 1101
2025-07-02 05:52:47.738
2025-07-02 05:52:47.745 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:47.752 g = []
2025-07-02 05:52:47.758 if alo < ahi:
2025-07-02 05:52:47.770 if blo < bhi:
2025-07-02 05:52:47.779 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:47.786 else:
2025-07-02 05:52:47.794 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:47.806 elif blo < bhi:
2025-07-02 05:52:47.814 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:47.822
2025-07-02 05:52:47.832 >       yield from g
2025-07-02 05:52:47.844
2025-07-02 05:52:47.853 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:47.863 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:47.877
2025-07-02 05:52:47.887 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:47.896 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:47.903 alo = 463, ahi = 1101
2025-07-02 05:52:47.911 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:47.917 blo = 463, bhi = 1101
2025-07-02 05:52:47.922
2025-07-02 05:52:47.928 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:47.934 r"""
2025-07-02 05:52:47.944 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:47.954 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:47.962 synch point, and intraline difference marking is done on the
2025-07-02 05:52:47.971 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:47.979
2025-07-02 05:52:47.987 Example:
2025-07-02 05:52:47.995
2025-07-02 05:52:48.007 >>> d = Differ()
2025-07-02 05:52:48.017 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:48.029 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:48.039 >>> print(''.join(results), end="")
2025-07-02 05:52:48.046 - abcDefghiJkl
2025-07-02 05:52:48.066 + abcdefGhijkl
2025-07-02 05:52:48.090 """
2025-07-02 05:52:48.100
2025-07-02 05:52:48.109 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:48.122 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:48.135 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:48.146 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:48.155 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:48.163
2025-07-02 05:52:48.170 # search for the pair that matches best without being identical
2025-07-02 05:52:48.179 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:48.190 # on junk -- unless we have to)
2025-07-02 05:52:48.203 for j in range(blo, bhi):
2025-07-02 05:52:48.214 bj = b[j]
2025-07-02 05:52:48.226 cruncher.set_seq2(bj)
2025-07-02 05:52:48.236 for i in range(alo, ahi):
2025-07-02 05:52:48.247 ai = a[i]
2025-07-02 05:52:48.261 if ai == bj:
2025-07-02 05:52:48.271 if eqi is None:
2025-07-02 05:52:48.280 eqi, eqj = i, j
2025-07-02 05:52:48.291 continue
2025-07-02 05:52:48.300 cruncher.set_seq1(ai)
2025-07-02 05:52:48.307 # computing similarity is expensive, so use the quick
2025-07-02 05:52:48.313 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:48.320 # compares by a factor of 3.
2025-07-02 05:52:48.326 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:48.337 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:48.346 # of the computation is cached by cruncher
2025-07-02 05:52:48.353 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:48.360 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:48.367 cruncher.ratio() > best_ratio:
2025-07-02 05:52:48.373 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:48.380 if best_ratio < cutoff:
2025-07-02 05:52:48.387 # no non-identical "pretty close" pair
2025-07-02 05:52:48.395 if eqi is None:
2025-07-02 05:52:48.403 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:48.411 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:48.419 return
2025-07-02 05:52:48.427 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:48.439 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:48.448 else:
2025-07-02 05:52:48.458 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:48.467 eqi = None
2025-07-02 05:52:48.475
2025-07-02 05:52:48.488 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:48.499 # identical
2025-07-02 05:52:48.509
2025-07-02 05:52:48.516 # pump out diffs from before the synch point
2025-07-02 05:52:48.522 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:48.533
2025-07-02 05:52:48.544 # do intraline marking on the synch pair
2025-07-02 05:52:48.554 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:48.565 if eqi is None:
2025-07-02 05:52:48.577 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:48.587 atags = btags = ""
2025-07-02 05:52:48.596 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:48.604 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:48.611 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:48.619 if tag == 'replace':
2025-07-02 05:52:48.630 atags += '^' * la
2025-07-02 05:52:48.639 btags += '^' * lb
2025-07-02 05:52:48.649 elif tag == 'delete':
2025-07-02 05:52:48.665 atags += '-' * la
2025-07-02 05:52:48.676 elif tag == 'insert':
2025-07-02 05:52:48.687 btags += '+' * lb
2025-07-02 05:52:48.696 elif tag == 'equal':
2025-07-02 05:52:48.704 atags += ' ' * la
2025-07-02 05:52:48.717 btags += ' ' * lb
2025-07-02 05:52:48.728 else:
2025-07-02 05:52:48.740 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:48.747 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:48.754 else:
2025-07-02 05:52:48.763 # the synch pair is identical
2025-07-02 05:52:48.775 yield '  ' + aelt
2025-07-02 05:52:48.785
2025-07-02 05:52:48.793 # pump out diffs from after the synch point
2025-07-02 05:52:48.804 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:48.813
2025-07-02 05:52:48.821 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:48.830 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:48.836
2025-07-02 05:52:48.846 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:48.857 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:48.871 alo = 466, ahi = 1101
2025-07-02 05:52:48.884 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:48.893 blo = 466, bhi = 1101
2025-07-02 05:52:48.901
2025-07-02 05:52:48.908 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:48.914 g = []
2025-07-02 05:52:48.920 if alo < ahi:
2025-07-02 05:52:48.926 if blo < bhi:
2025-07-02 05:52:48.932 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:48.938 else:
2025-07-02 05:52:48.945 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:48.951 elif blo < bhi:
2025-07-02 05:52:48.958 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:48.963
2025-07-02 05:52:48.970 >       yield from g
2025-07-02 05:52:48.980
2025-07-02 05:52:48.990 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:49.002 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:49.011
2025-07-02 05:52:49.019 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:49.025 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:49.030 alo = 466, ahi = 1101
2025-07-02 05:52:49.036 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:49.042 blo = 466, bhi = 1101
2025-07-02 05:52:49.046
2025-07-02 05:52:49.051 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:49.056 r"""
2025-07-02 05:52:49.061 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:49.066 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:49.070 synch point, and intraline difference marking is done on the
2025-07-02 05:52:49.075 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:49.081
2025-07-02 05:52:49.087 Example:
2025-07-02 05:52:49.092
2025-07-02 05:52:49.098 >>> d = Differ()
2025-07-02 05:52:49.107 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:49.113 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:49.119 >>> print(''.join(results), end="")
2025-07-02 05:52:49.124 - abcDefghiJkl
2025-07-02 05:52:49.134 + abcdefGhijkl
2025-07-02 05:52:49.145 """
2025-07-02 05:52:49.151
2025-07-02 05:52:49.157 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:49.167 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:49.177 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:49.184 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:49.190 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:49.200
2025-07-02 05:52:49.211 # search for the pair that matches best without being identical
2025-07-02 05:52:49.219 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:49.227 # on junk -- unless we have to)
2025-07-02 05:52:49.235 for j in range(blo, bhi):
2025-07-02 05:52:49.240 bj = b[j]
2025-07-02 05:52:49.245 cruncher.set_seq2(bj)
2025-07-02 05:52:49.251 for i in range(alo, ahi):
2025-07-02 05:52:49.258 ai = a[i]
2025-07-02 05:52:49.265 if ai == bj:
2025-07-02 05:52:49.273 if eqi is None:
2025-07-02 05:52:49.280 eqi, eqj = i, j
2025-07-02 05:52:49.286 continue
2025-07-02 05:52:49.297 cruncher.set_seq1(ai)
2025-07-02 05:52:49.307 # computing similarity is expensive, so use the quick
2025-07-02 05:52:49.315 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:49.323 # compares by a factor of 3.
2025-07-02 05:52:49.331 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:49.344 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:49.353 # of the computation is cached by cruncher
2025-07-02 05:52:49.360 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:49.368 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:49.375 cruncher.ratio() > best_ratio:
2025-07-02 05:52:49.381 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:49.386 if best_ratio < cutoff:
2025-07-02 05:52:49.395 # no non-identical "pretty close" pair
2025-07-02 05:52:49.405 if eqi is None:
2025-07-02 05:52:49.414 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:49.422 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:49.429 return
2025-07-02 05:52:49.437 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:49.443 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:49.449 else:
2025-07-02 05:52:49.462 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:49.470 eqi = None
2025-07-02 05:52:49.478
2025-07-02 05:52:49.486 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:49.492 # identical
2025-07-02 05:52:49.498
2025-07-02 05:52:49.504 # pump out diffs from before the synch point
2025-07-02 05:52:49.510 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:49.517
2025-07-02 05:52:49.524 # do intraline marking on the synch pair
2025-07-02 05:52:49.531 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:49.539 if eqi is None:
2025-07-02 05:52:49.553 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:49.564 atags = btags = ""
2025-07-02 05:52:49.571 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:49.577 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:49.583 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:49.590 if tag == 'replace':
2025-07-02 05:52:49.596 atags += '^' * la
2025-07-02 05:52:49.602 btags += '^' * lb
2025-07-02 05:52:49.611 elif tag == 'delete':
2025-07-02 05:52:49.621 atags += '-' * la
2025-07-02 05:52:49.629 elif tag == 'insert':
2025-07-02 05:52:49.635 btags += '+' * lb
2025-07-02 05:52:49.643 elif tag == 'equal':
2025-07-02 05:52:49.654 atags += ' ' * la
2025-07-02 05:52:49.662 btags += ' ' * lb
2025-07-02 05:52:49.671 else:
2025-07-02 05:52:49.683 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:49.693 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:49.703 else:
2025-07-02 05:52:49.710 # the synch pair is identical
2025-07-02 05:52:49.717 yield '  ' + aelt
2025-07-02 05:52:49.728
2025-07-02 05:52:49.736 # pump out diffs from after the synch point
2025-07-02 05:52:49.744 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:49.751
2025-07-02 05:52:49.758 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:49.766 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:49.777
2025-07-02 05:52:49.786 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:49.795 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:49.802 alo = 467, ahi = 1101
2025-07-02 05:52:49.809 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:49.814 blo = 467, bhi = 1101
2025-07-02 05:52:49.821
2025-07-02 05:52:49.827 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:49.834 g = []
2025-07-02 05:52:49.841 if alo < ahi:
2025-07-02 05:52:49.848 if blo < bhi:
2025-07-02 05:52:49.860 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:49.871 else:
2025-07-02 05:52:49.883 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:49.892 elif blo < bhi:
2025-07-02 05:52:49.899 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:49.905
2025-07-02 05:52:49.910 >       yield from g
2025-07-02 05:52:49.914
2025-07-02 05:52:49.919 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:49.924 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:49.928
2025-07-02 05:52:49.933 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:49.939 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:49.945 alo = 467, ahi = 1101
2025-07-02 05:52:49.952 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:49.958 blo = 467, bhi = 1101
2025-07-02 05:52:49.965
2025-07-02 05:52:49.977 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:49.987 r"""
2025-07-02 05:52:49.995 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:50.002 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:50.009 synch point, and intraline difference marking is done on the
2025-07-02 05:52:50.016 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:50.023
2025-07-02 05:52:50.032 Example:
2025-07-02 05:52:50.045
2025-07-02 05:52:50.055 >>> d = Differ()
2025-07-02 05:52:50.064 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:50.071 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:50.077 >>> print(''.join(results), end="")
2025-07-02 05:52:50.083 - abcDefghiJkl
2025-07-02 05:52:50.096 + abcdefGhijkl
2025-07-02 05:52:50.109 """
2025-07-02 05:52:50.116
2025-07-02 05:52:50.124 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:50.132 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:50.142 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:50.151 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:50.158 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:50.164
2025-07-02 05:52:50.175 # search for the pair that matches best without being identical
2025-07-02 05:52:50.187 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:50.195 # on junk -- unless we have to)
2025-07-02 05:52:50.202 for j in range(blo, bhi):
2025-07-02 05:52:50.209 bj = b[j]
2025-07-02 05:52:50.215 cruncher.set_seq2(bj)
2025-07-02 05:52:50.222 for i in range(alo, ahi):
2025-07-02 05:52:50.228 ai = a[i]
2025-07-02 05:52:50.235 if ai == bj:
2025-07-02 05:52:50.243 if eqi is None:
2025-07-02 05:52:50.250 eqi, eqj = i, j
2025-07-02 05:52:50.261 continue
2025-07-02 05:52:50.271 cruncher.set_seq1(ai)
2025-07-02 05:52:50.278 # computing similarity is expensive, so use the quick
2025-07-02 05:52:50.285 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:50.292 # compares by a factor of 3.
2025-07-02 05:52:50.299 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:50.306 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:50.315 # of the computation is cached by cruncher
2025-07-02 05:52:50.328 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:50.338 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:50.345 cruncher.ratio() > best_ratio:
2025-07-02 05:52:50.351 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:50.357 if best_ratio < cutoff:
2025-07-02 05:52:50.363 # no non-identical "pretty close" pair
2025-07-02 05:52:50.369 if eqi is None:
2025-07-02 05:52:50.375 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:50.382 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:50.388 return
2025-07-02 05:52:50.396 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:50.403 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:50.411 else:
2025-07-02 05:52:50.420 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:50.430 eqi = None
2025-07-02 05:52:50.439
2025-07-02 05:52:50.446 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:50.452 # identical
2025-07-02 05:52:50.459
2025-07-02 05:52:50.465 # pump out diffs from before the synch point
2025-07-02 05:52:50.471 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:50.478
2025-07-02 05:52:50.489 # do intraline marking on the synch pair
2025-07-02 05:52:50.501 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:50.513 if eqi is None:
2025-07-02 05:52:50.520 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:50.526 atags = btags = ""
2025-07-02 05:52:50.540 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:50.548 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:50.556 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:50.563 if tag == 'replace':
2025-07-02 05:52:50.569 atags += '^' * la
2025-07-02 05:52:50.575 btags += '^' * lb
2025-07-02 05:52:50.581 elif tag == 'delete':
2025-07-02 05:52:50.587 atags += '-' * la
2025-07-02 05:52:50.599 elif tag == 'insert':
2025-07-02 05:52:50.608 btags += '+' * lb
2025-07-02 05:52:50.617 elif tag == 'equal':
2025-07-02 05:52:50.627 atags += ' ' * la
2025-07-02 05:52:50.638 btags += ' ' * lb
2025-07-02 05:52:50.647 else:
2025-07-02 05:52:50.655 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:50.663 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:50.670 else:
2025-07-02 05:52:50.676 # the synch pair is identical
2025-07-02 05:52:50.682 yield '  ' + aelt
2025-07-02 05:52:50.687
2025-07-02 05:52:50.693 # pump out diffs from after the synch point
2025-07-02 05:52:50.707 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:50.719
2025-07-02 05:52:50.727 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:50.735 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:50.742
2025-07-02 05:52:50.749 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:50.762 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:50.771 alo = 468, ahi = 1101
2025-07-02 05:52:50.778 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:50.790 blo = 468, bhi = 1101
2025-07-02 05:52:50.800
2025-07-02 05:52:50.808 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:50.816 g = []
2025-07-02 05:52:50.823 if alo < ahi:
2025-07-02 05:52:50.831 if blo < bhi:
2025-07-02 05:52:50.843 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:50.853 else:
2025-07-02 05:52:50.860 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:50.867 elif blo < bhi:
2025-07-02 05:52:50.874 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:50.880
2025-07-02 05:52:50.886 >       yield from g
2025-07-02 05:52:50.891
2025-07-02 05:52:50.897 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:50.902 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:50.908
2025-07-02 05:52:50.921 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:50.934 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:50.946 alo = 468, ahi = 1101
2025-07-02 05:52:50.959 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:50.972 blo = 468, bhi = 1101
2025-07-02 05:52:50.981
2025-07-02 05:52:50.989 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:50.996 r"""
2025-07-02 05:52:51.003 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:51.017 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:51.030 synch point, and intraline difference marking is done on the
2025-07-02 05:52:51.039 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:51.047
2025-07-02 05:52:51.056 Example:
2025-07-02 05:52:51.063
2025-07-02 05:52:51.069 >>> d = Differ()
2025-07-02 05:52:51.076 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:51.082 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:51.087 >>> print(''.join(results), end="")
2025-07-02 05:52:51.094 - abcDefghiJkl
2025-07-02 05:52:51.110 + abcdefGhijkl
2025-07-02 05:52:51.127 """
2025-07-02 05:52:51.137
2025-07-02 05:52:51.145 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:51.152 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:51.158 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:51.163 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:51.171 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:51.180
2025-07-02 05:52:51.188 # search for the pair that matches best without being identical
2025-07-02 05:52:51.195 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:51.201 # on junk -- unless we have to)
2025-07-02 05:52:51.207 for j in range(blo, bhi):
2025-07-02 05:52:51.213 bj = b[j]
2025-07-02 05:52:51.218 cruncher.set_seq2(bj)
2025-07-02 05:52:51.224 for i in range(alo, ahi):
2025-07-02 05:52:51.230 ai = a[i]
2025-07-02 05:52:51.243 if ai == bj:
2025-07-02 05:52:51.250 if eqi is None:
2025-07-02 05:52:51.260 eqi, eqj = i, j
2025-07-02 05:52:51.268 continue
2025-07-02 05:52:51.275 cruncher.set_seq1(ai)
2025-07-02 05:52:51.283 # computing similarity is expensive, so use the quick
2025-07-02 05:52:51.297 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:51.306 # compares by a factor of 3.
2025-07-02 05:52:51.316 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:51.325 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:51.336 # of the computation is cached by cruncher
2025-07-02 05:52:51.345 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:51.358 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:51.371 cruncher.ratio() > best_ratio:
2025-07-02 05:52:51.379 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:51.386 if best_ratio < cutoff:
2025-07-02 05:52:51.392 # no non-identical "pretty close" pair
2025-07-02 05:52:51.398 if eqi is None:
2025-07-02 05:52:51.407 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:51.414 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:51.420 return
2025-07-02 05:52:51.426 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:51.433 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:51.440 else:
2025-07-02 05:52:51.447 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:51.453 eqi = None
2025-07-02 05:52:51.458
2025-07-02 05:52:51.463 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:51.469 # identical
2025-07-02 05:52:51.474
2025-07-02 05:52:51.480 # pump out diffs from before the synch point
2025-07-02 05:52:51.490 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:51.503
2025-07-02 05:52:51.510 # do intraline marking on the synch pair
2025-07-02 05:52:51.517 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:51.523 if eqi is None:
2025-07-02 05:52:51.536 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:51.547 atags = btags = ""
2025-07-02 05:52:51.555 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:51.563 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:51.571 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:51.582 if tag == 'replace':
2025-07-02 05:52:51.591 atags += '^' * la
2025-07-02 05:52:51.601 btags += '^' * lb
2025-07-02 05:52:51.609 elif tag == 'delete':
2025-07-02 05:52:51.615 atags += '-' * la
2025-07-02 05:52:51.621 elif tag == 'insert':
2025-07-02 05:52:51.627 btags += '+' * lb
2025-07-02 05:52:51.634 elif tag == 'equal':
2025-07-02 05:52:51.646 atags += ' ' * la
2025-07-02 05:52:51.657 btags += ' ' * lb
2025-07-02 05:52:51.665 else:
2025-07-02 05:52:51.677 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:51.687 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:51.695 else:
2025-07-02 05:52:51.702 # the synch pair is identical
2025-07-02 05:52:51.715 yield '  ' + aelt
2025-07-02 05:52:51.724
2025-07-02 05:52:51.733 # pump out diffs from after the synch point
2025-07-02 05:52:51.740 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:51.750
2025-07-02 05:52:51.769 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:51.778 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:51.785
2025-07-02 05:52:51.792 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:51.797 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:51.803 alo = 469, ahi = 1101
2025-07-02 05:52:51.810 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:51.818 blo = 469, bhi = 1101
2025-07-02 05:52:51.823
2025-07-02 05:52:51.830 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:51.836 g = []
2025-07-02 05:52:51.841 if alo < ahi:
2025-07-02 05:52:51.847 if blo < bhi:
2025-07-02 05:52:51.852 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:51.858 else:
2025-07-02 05:52:51.864 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:51.870 elif blo < bhi:
2025-07-02 05:52:51.878 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:51.884
2025-07-02 05:52:51.889 >       yield from g
2025-07-02 05:52:51.894
2025-07-02 05:52:51.900 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:51.906 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:51.919
2025-07-02 05:52:51.926 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:51.935 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:51.944 alo = 469, ahi = 1101
2025-07-02 05:52:51.957 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:51.966 blo = 469, bhi = 1101
2025-07-02 05:52:51.974
2025-07-02 05:52:51.985 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:51.993 r"""
2025-07-02 05:52:52.000 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:52.008 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:52.014 synch point, and intraline difference marking is done on the
2025-07-02 05:52:52.021 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:52.026
2025-07-02 05:52:52.038 Example:
2025-07-02 05:52:52.048
2025-07-02 05:52:52.056 >>> d = Differ()
2025-07-02 05:52:52.062 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:52.072 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:52.079 >>> print(''.join(results), end="")
2025-07-02 05:52:52.087 - abcDefghiJkl
2025-07-02 05:52:52.110 + abcdefGhijkl
2025-07-02 05:52:52.135 """
2025-07-02 05:52:52.143
2025-07-02 05:52:52.151 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:52.158 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:52.167 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:52.178 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:52.188 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:52.196
2025-07-02 05:52:52.204 # search for the pair that matches best without being identical
2025-07-02 05:52:52.211 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:52.217 # on junk -- unless we have to)
2025-07-02 05:52:52.223 for j in range(blo, bhi):
2025-07-02 05:52:52.230 bj = b[j]
2025-07-02 05:52:52.236 cruncher.set_seq2(bj)
2025-07-02 05:52:52.242 for i in range(alo, ahi):
2025-07-02 05:52:52.248 ai = a[i]
2025-07-02 05:52:52.254 if ai == bj:
2025-07-02 05:52:52.266 if eqi is None:
2025-07-02 05:52:52.276 eqi, eqj = i, j
2025-07-02 05:52:52.287 continue
2025-07-02 05:52:52.300 cruncher.set_seq1(ai)
2025-07-02 05:52:52.309 # computing similarity is expensive, so use the quick
2025-07-02 05:52:52.316 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:52.323 # compares by a factor of 3.
2025-07-02 05:52:52.328 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:52.332 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:52.338 # of the computation is cached by cruncher
2025-07-02 05:52:52.343 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:52.348 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:52.353 cruncher.ratio() > best_ratio:
2025-07-02 05:52:52.358 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:52.363 if best_ratio < cutoff:
2025-07-02 05:52:52.375 # no non-identical "pretty close" pair
2025-07-02 05:52:52.387 if eqi is None:
2025-07-02 05:52:52.397 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:52.410 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:52.418 return
2025-07-02 05:52:52.425 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:52.437 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:52.447 else:
2025-07-02 05:52:52.455 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:52.466 eqi = None
2025-07-02 05:52:52.478
2025-07-02 05:52:52.488 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:52.496 # identical
2025-07-02 05:52:52.504
2025-07-02 05:52:52.510 # pump out diffs from before the synch point
2025-07-02 05:52:52.517 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:52.523
2025-07-02 05:52:52.531 # do intraline marking on the synch pair
2025-07-02 05:52:52.536 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:52.541 if eqi is None:
2025-07-02 05:52:52.546 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:52.552 atags = btags = ""
2025-07-02 05:52:52.558 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:52.565 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:52.572 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:52.579 if tag == 'replace':
2025-07-02 05:52:52.584 atags += '^' * la
2025-07-02 05:52:52.590 btags += '^' * lb
2025-07-02 05:52:52.599 elif tag == 'delete':
2025-07-02 05:52:52.610 atags += '-' * la
2025-07-02 05:52:52.624 elif tag == 'insert':
2025-07-02 05:52:52.635 btags += '+' * lb
2025-07-02 05:52:52.645 elif tag == 'equal':
2025-07-02 05:52:52.654 atags += ' ' * la
2025-07-02 05:52:52.666 btags += ' ' * lb
2025-07-02 05:52:52.675 else:
2025-07-02 05:52:52.683 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:52.690 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:52.696 else:
2025-07-02 05:52:52.701 # the synch pair is identical
2025-07-02 05:52:52.707 yield '  ' + aelt
2025-07-02 05:52:52.713
2025-07-02 05:52:52.718 # pump out diffs from after the synch point
2025-07-02 05:52:52.725 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:52.730
2025-07-02 05:52:52.736 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:52.742 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:52.752
2025-07-02 05:52:52.761 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:52.769 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:52.775 alo = 470, ahi = 1101
2025-07-02 05:52:52.783 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:52.788 blo = 470, bhi = 1101
2025-07-02 05:52:52.794
2025-07-02 05:52:52.800 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:52.805 g = []
2025-07-02 05:52:52.812 if alo < ahi:
2025-07-02 05:52:52.818 if blo < bhi:
2025-07-02 05:52:52.824 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:52.831 else:
2025-07-02 05:52:52.841 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:52.850 elif blo < bhi:
2025-07-02 05:52:52.858 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:52.866
2025-07-02 05:52:52.875 >       yield from g
2025-07-02 05:52:52.887
2025-07-02 05:52:52.895 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:52.903 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:52.910
2025-07-02 05:52:52.917 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:52.925 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:52.931 alo = 470, ahi = 1101
2025-07-02 05:52:52.938 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:52.944 blo = 470, bhi = 1101
2025-07-02 05:52:52.951
2025-07-02 05:52:52.962 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:52.971 r"""
2025-07-02 05:52:52.980 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:52.987 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:52.994 synch point, and intraline difference marking is done on the
2025-07-02 05:52:53.005 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:53.016
2025-07-02 05:52:53.025 Example:
2025-07-02 05:52:53.033
2025-07-02 05:52:53.039 >>> d = Differ()
2025-07-02 05:52:53.046 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:53.052 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:53.058 >>> print(''.join(results), end="")
2025-07-02 05:52:53.064 - abcDefghiJkl
2025-07-02 05:52:53.076 + abcdefGhijkl
2025-07-02 05:52:53.088 """
2025-07-02 05:52:53.094
2025-07-02 05:52:53.100 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:53.107 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:53.113 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:53.118 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:53.128 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:53.138
2025-07-02 05:52:53.145 # search for the pair that matches best without being identical
2025-07-02 05:52:53.152 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:53.158 # on junk -- unless we have to)
2025-07-02 05:52:53.163 for j in range(blo, bhi):
2025-07-02 05:52:53.170 bj = b[j]
2025-07-02 05:52:53.176 cruncher.set_seq2(bj)
2025-07-02 05:52:53.182 for i in range(alo, ahi):
2025-07-02 05:52:53.188 ai = a[i]
2025-07-02 05:52:53.195 if ai == bj:
2025-07-02 05:52:53.206 if eqi is None:
2025-07-02 05:52:53.214 eqi, eqj = i, j
2025-07-02 05:52:53.221 continue
2025-07-02 05:52:53.228 cruncher.set_seq1(ai)
2025-07-02 05:52:53.235 # computing similarity is expensive, so use the quick
2025-07-02 05:52:53.243 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:53.253 # compares by a factor of 3.
2025-07-02 05:52:53.263 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:53.274 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:53.280 # of the computation is cached by cruncher
2025-07-02 05:52:53.287 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:53.295 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:53.308 cruncher.ratio() > best_ratio:
2025-07-02 05:52:53.319 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:53.331 if best_ratio < cutoff:
2025-07-02 05:52:53.340 # no non-identical "pretty close" pair
2025-07-02 05:52:53.348 if eqi is None:
2025-07-02 05:52:53.355 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:53.363 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:53.374 return
2025-07-02 05:52:53.381 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:53.389 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:53.400 else:
2025-07-02 05:52:53.411 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:53.424 eqi = None
2025-07-02 05:52:53.432
2025-07-02 05:52:53.440 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:53.447 # identical
2025-07-02 05:52:53.454
2025-07-02 05:52:53.466 # pump out diffs from before the synch point
2025-07-02 05:52:53.479 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:53.490
2025-07-02 05:52:53.499 # do intraline marking on the synch pair
2025-07-02 05:52:53.513 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:53.525 if eqi is None:
2025-07-02 05:52:53.539 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:53.552 atags = btags = ""
2025-07-02 05:52:53.564 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:53.577 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:53.590 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:53.598 if tag == 'replace':
2025-07-02 05:52:53.606 atags += '^' * la
2025-07-02 05:52:53.616 btags += '^' * lb
2025-07-02 05:52:53.626 elif tag == 'delete':
2025-07-02 05:52:53.635 atags += '-' * la
2025-07-02 05:52:53.648 elif tag == 'insert':
2025-07-02 05:52:53.659 btags += '+' * lb
2025-07-02 05:52:53.668 elif tag == 'equal':
2025-07-02 05:52:53.676 atags += ' ' * la
2025-07-02 05:52:53.683 btags += ' ' * lb
2025-07-02 05:52:53.689 else:
2025-07-02 05:52:53.694 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:53.702 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:53.713 else:
2025-07-02 05:52:53.722 # the synch pair is identical
2025-07-02 05:52:53.731 yield '  ' + aelt
2025-07-02 05:52:53.740
2025-07-02 05:52:53.748 # pump out diffs from after the synch point
2025-07-02 05:52:53.755 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:53.763
2025-07-02 05:52:53.773 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:53.785 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:53.799
2025-07-02 05:52:53.809 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:53.822 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:53.832 alo = 471, ahi = 1101
2025-07-02 05:52:53.843 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:53.851 blo = 471, bhi = 1101
2025-07-02 05:52:53.858
2025-07-02 05:52:53.865 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:53.871 g = []
2025-07-02 05:52:53.877 if alo < ahi:
2025-07-02 05:52:53.883 if blo < bhi:
2025-07-02 05:52:53.892 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:53.903 else:
2025-07-02 05:52:53.911 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:53.917 elif blo < bhi:
2025-07-02 05:52:53.931 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:53.941
2025-07-02 05:52:53.950 >       yield from g
2025-07-02 05:52:53.963
2025-07-02 05:52:53.975 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:53.986 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:53.999
2025-07-02 05:52:54.006 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:54.014 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:54.021 alo = 471, ahi = 1101
2025-07-02 05:52:54.027 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:54.033 blo = 471, bhi = 1101
2025-07-02 05:52:54.038
2025-07-02 05:52:54.049 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:54.058 r"""
2025-07-02 05:52:54.066 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:54.074 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:54.081 synch point, and intraline difference marking is done on the
2025-07-02 05:52:54.087 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:54.093
2025-07-02 05:52:54.098 Example:
2025-07-02 05:52:54.108
2025-07-02 05:52:54.117 >>> d = Differ()
2025-07-02 05:52:54.125 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:54.133 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:54.140 >>> print(''.join(results), end="")
2025-07-02 05:52:54.146 - abcDefghiJkl
2025-07-02 05:52:54.161 + abcdefGhijkl
2025-07-02 05:52:54.181 """
2025-07-02 05:52:54.192
2025-07-02 05:52:54.201 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:54.212 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:54.221 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:54.228 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:54.236 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:54.245
2025-07-02 05:52:54.257 # search for the pair that matches best without being identical
2025-07-02 05:52:54.266 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:54.281 # on junk -- unless we have to)
2025-07-02 05:52:54.294 for j in range(blo, bhi):
2025-07-02 05:52:54.303 bj = b[j]
2025-07-02 05:52:54.311 cruncher.set_seq2(bj)
2025-07-02 05:52:54.319 for i in range(alo, ahi):
2025-07-02 05:52:54.327 ai = a[i]
2025-07-02 05:52:54.339 if ai == bj:
2025-07-02 05:52:54.348 if eqi is None:
2025-07-02 05:52:54.356 eqi, eqj = i, j
2025-07-02 05:52:54.362 continue
2025-07-02 05:52:54.369 cruncher.set_seq1(ai)
2025-07-02 05:52:54.375 # computing similarity is expensive, so use the quick
2025-07-02 05:52:54.380 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:54.386 # compares by a factor of 3.
2025-07-02 05:52:54.397 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:54.404 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:54.411 # of the computation is cached by cruncher
2025-07-02 05:52:54.417 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:54.424 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:54.434 cruncher.ratio() > best_ratio:
2025-07-02 05:52:54.440 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:54.447 if best_ratio < cutoff:
2025-07-02 05:52:54.459 # no non-identical "pretty close" pair
2025-07-02 05:52:54.470 if eqi is None:
2025-07-02 05:52:54.482 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:54.496 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:54.508 return
2025-07-02 05:52:54.519 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:54.530 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:54.539 else:
2025-07-02 05:52:54.548 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:54.554 eqi = None
2025-07-02 05:52:54.560
2025-07-02 05:52:54.567 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:54.573 # identical
2025-07-02 05:52:54.580
2025-07-02 05:52:54.591 # pump out diffs from before the synch point
2025-07-02 05:52:54.599 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:54.608
2025-07-02 05:52:54.615 # do intraline marking on the synch pair
2025-07-02 05:52:54.622 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:54.628 if eqi is None:
2025-07-02 05:52:54.633 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:54.638 atags = btags = ""
2025-07-02 05:52:54.643 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:54.649 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:54.655 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:54.661 if tag == 'replace':
2025-07-02 05:52:54.667 atags += '^' * la
2025-07-02 05:52:54.672 btags += '^' * lb
2025-07-02 05:52:54.677 elif tag == 'delete':
2025-07-02 05:52:54.682 atags += '-' * la
2025-07-02 05:52:54.689 elif tag == 'insert':
2025-07-02 05:52:54.696 btags += '+' * lb
2025-07-02 05:52:54.702 elif tag == 'equal':
2025-07-02 05:52:54.707 atags += ' ' * la
2025-07-02 05:52:54.713 btags += ' ' * lb
2025-07-02 05:52:54.718 else:
2025-07-02 05:52:54.729 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:54.739 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:54.748 else:
2025-07-02 05:52:54.755 # the synch pair is identical
2025-07-02 05:52:54.770 yield '  ' + aelt
2025-07-02 05:52:54.781
2025-07-02 05:52:54.790 # pump out diffs from after the synch point
2025-07-02 05:52:54.799 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:54.813
2025-07-02 05:52:54.823 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:54.832 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:54.839
2025-07-02 05:52:54.846 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:54.854 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:54.865 alo = 472, ahi = 1101
2025-07-02 05:52:54.875 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:54.885 blo = 472, bhi = 1101
2025-07-02 05:52:54.893
2025-07-02 05:52:54.900 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:54.907 g = []
2025-07-02 05:52:54.915 if alo < ahi:
2025-07-02 05:52:54.923 if blo < bhi:
2025-07-02 05:52:54.935 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:54.945 else:
2025-07-02 05:52:54.953 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:54.966 elif blo < bhi:
2025-07-02 05:52:54.974 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:54.982
2025-07-02 05:52:54.995 >       yield from g
2025-07-02 05:52:55.008
2025-07-02 05:52:55.019 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:55.028 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:55.036
2025-07-02 05:52:55.044 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:55.053 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:55.059 alo = 472, ahi = 1101
2025-07-02 05:52:55.067 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:55.078 blo = 472, bhi = 1101
2025-07-02 05:52:55.087
2025-07-02 05:52:55.094 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:55.104 r"""
2025-07-02 05:52:55.114 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:55.123 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:55.132 synch point, and intraline difference marking is done on the
2025-07-02 05:52:55.144 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:55.158
2025-07-02 05:52:55.171 Example:
2025-07-02 05:52:55.182
2025-07-02 05:52:55.194 >>> d = Differ()
2025-07-02 05:52:55.206 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:55.217 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:55.229 >>> print(''.join(results), end="")
2025-07-02 05:52:55.241 - abcDefghiJkl
2025-07-02 05:52:55.267 + abcdefGhijkl
2025-07-02 05:52:55.296 """
2025-07-02 05:52:55.308
2025-07-02 05:52:55.320 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:55.330 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:55.341 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:55.353 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:55.365 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:55.373
2025-07-02 05:52:55.382 # search for the pair that matches best without being identical
2025-07-02 05:52:55.388 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:55.394 # on junk -- unless we have to)
2025-07-02 05:52:55.399 for j in range(blo, bhi):
2025-07-02 05:52:55.404 bj = b[j]
2025-07-02 05:52:55.409 cruncher.set_seq2(bj)
2025-07-02 05:52:55.420 for i in range(alo, ahi):
2025-07-02 05:52:55.429 ai = a[i]
2025-07-02 05:52:55.437 if ai == bj:
2025-07-02 05:52:55.444 if eqi is None:
2025-07-02 05:52:55.451 eqi, eqj = i, j
2025-07-02 05:52:55.457 continue
2025-07-02 05:52:55.467 cruncher.set_seq1(ai)
2025-07-02 05:52:55.476 # computing similarity is expensive, so use the quick
2025-07-02 05:52:55.483 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:55.491 # compares by a factor of 3.
2025-07-02 05:52:55.501 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:55.509 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:55.516 # of the computation is cached by cruncher
2025-07-02 05:52:55.522 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:55.533 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:55.542 cruncher.ratio() > best_ratio:
2025-07-02 05:52:55.549 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:55.559 if best_ratio < cutoff:
2025-07-02 05:52:55.572 # no non-identical "pretty close" pair
2025-07-02 05:52:55.581 if eqi is None:
2025-07-02 05:52:55.593 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:55.603 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:55.611 return
2025-07-02 05:52:55.619 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:55.625 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:55.631 else:
2025-07-02 05:52:55.643 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:55.653 eqi = None
2025-07-02 05:52:55.661
2025-07-02 05:52:55.668 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:55.676 # identical
2025-07-02 05:52:55.686
2025-07-02 05:52:55.694 # pump out diffs from before the synch point
2025-07-02 05:52:55.701 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:55.708
2025-07-02 05:52:55.714 # do intraline marking on the synch pair
2025-07-02 05:52:55.719 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:55.724 if eqi is None:
2025-07-02 05:52:55.729 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:55.734 atags = btags = ""
2025-07-02 05:52:55.745 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:55.755 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:55.763 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:55.770 if tag == 'replace':
2025-07-02 05:52:55.778 atags += '^' * la
2025-07-02 05:52:55.787 btags += '^' * lb
2025-07-02 05:52:55.799 elif tag == 'delete':
2025-07-02 05:52:55.809 atags += '-' * la
2025-07-02 05:52:55.817 elif tag == 'insert':
2025-07-02 05:52:55.824 btags += '+' * lb
2025-07-02 05:52:55.833 elif tag == 'equal':
2025-07-02 05:52:55.839 atags += ' ' * la
2025-07-02 05:52:55.845 btags += ' ' * lb
2025-07-02 05:52:55.855 else:
2025-07-02 05:52:55.866 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:55.874 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:55.882 else:
2025-07-02 05:52:55.892 # the synch pair is identical
2025-07-02 05:52:55.900 yield '  ' + aelt
2025-07-02 05:52:55.907
2025-07-02 05:52:55.913 # pump out diffs from after the synch point
2025-07-02 05:52:55.919 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:55.925
2025-07-02 05:52:55.931 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:55.937 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:55.943
2025-07-02 05:52:55.951 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:55.962 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:55.971 alo = 473, ahi = 1101
2025-07-02 05:52:55.981 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:55.987 blo = 473, bhi = 1101
2025-07-02 05:52:55.993
2025-07-02 05:52:55.999 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:56.006 g = []
2025-07-02 05:52:56.013 if alo < ahi:
2025-07-02 05:52:56.020 if blo < bhi:
2025-07-02 05:52:56.025 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:56.030 else:
2025-07-02 05:52:56.036 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:56.042 elif blo < bhi:
2025-07-02 05:52:56.048 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:56.055
2025-07-02 05:52:56.067 >       yield from g
2025-07-02 05:52:56.077
2025-07-02 05:52:56.086 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:56.094 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:56.105
2025-07-02 05:52:56.115 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:56.124 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:56.135 alo = 473, ahi = 1101
2025-07-02 05:52:56.147 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:56.159 blo = 473, bhi = 1101
2025-07-02 05:52:56.171
2025-07-02 05:52:56.185 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:56.194 r"""
2025-07-02 05:52:56.206 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:56.218 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:56.228 synch point, and intraline difference marking is done on the
2025-07-02 05:52:56.237 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:56.244
2025-07-02 05:52:56.250 Example:
2025-07-02 05:52:56.255
2025-07-02 05:52:56.263 >>> d = Differ()
2025-07-02 05:52:56.273 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:56.286 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:56.294 >>> print(''.join(results), end="")
2025-07-02 05:52:56.301 - abcDefghiJkl
2025-07-02 05:52:56.313 + abcdefGhijkl
2025-07-02 05:52:56.323 """
2025-07-02 05:52:56.328
2025-07-02 05:52:56.335 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:56.346 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:56.358 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:56.369 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:56.381 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:56.391
2025-07-02 05:52:56.400 # search for the pair that matches best without being identical
2025-07-02 05:52:56.408 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:56.414 # on junk -- unless we have to)
2025-07-02 05:52:56.421 for j in range(blo, bhi):
2025-07-02 05:52:56.427 bj = b[j]
2025-07-02 05:52:56.435 cruncher.set_seq2(bj)
2025-07-02 05:52:56.446 for i in range(alo, ahi):
2025-07-02 05:52:56.456 ai = a[i]
2025-07-02 05:52:56.463 if ai == bj:
2025-07-02 05:52:56.469 if eqi is None:
2025-07-02 05:52:56.474 eqi, eqj = i, j
2025-07-02 05:52:56.479 continue
2025-07-02 05:52:56.484 cruncher.set_seq1(ai)
2025-07-02 05:52:56.489 # computing similarity is expensive, so use the quick
2025-07-02 05:52:56.497 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:56.507 # compares by a factor of 3.
2025-07-02 05:52:56.517 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:56.529 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:56.540 # of the computation is cached by cruncher
2025-07-02 05:52:56.549 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:56.562 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:56.572 cruncher.ratio() > best_ratio:
2025-07-02 05:52:56.579 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:56.585 if best_ratio < cutoff:
2025-07-02 05:52:56.590 # no non-identical "pretty close" pair
2025-07-02 05:52:56.595 if eqi is None:
2025-07-02 05:52:56.600 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:56.605 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:56.610 return
2025-07-02 05:52:56.616 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:56.622 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:56.629 else:
2025-07-02 05:52:56.635 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:56.640 eqi = None
2025-07-02 05:52:56.644
2025-07-02 05:52:56.650 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:56.656 # identical
2025-07-02 05:52:56.662
2025-07-02 05:52:56.671 # pump out diffs from before the synch point
2025-07-02 05:52:56.679 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:56.686
2025-07-02 05:52:56.692 # do intraline marking on the synch pair
2025-07-02 05:52:56.699 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:56.708 if eqi is None:
2025-07-02 05:52:56.716 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:56.723 atags = btags = ""
2025-07-02 05:52:56.733 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:56.745 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:56.756 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:56.764 if tag == 'replace':
2025-07-02 05:52:56.772 atags += '^' * la
2025-07-02 05:52:56.779 btags += '^' * lb
2025-07-02 05:52:56.787 elif tag == 'delete':
2025-07-02 05:52:56.792 atags += '-' * la
2025-07-02 05:52:56.798 elif tag == 'insert':
2025-07-02 05:52:56.805 btags += '+' * lb
2025-07-02 05:52:56.812 elif tag == 'equal':
2025-07-02 05:52:56.818 atags += ' ' * la
2025-07-02 05:52:56.826 btags += ' ' * lb
2025-07-02 05:52:56.835 else:
2025-07-02 05:52:56.842 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:56.849 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:56.855 else:
2025-07-02 05:52:56.861 # the synch pair is identical
2025-07-02 05:52:56.868 yield '  ' + aelt
2025-07-02 05:52:56.874
2025-07-02 05:52:56.883 # pump out diffs from after the synch point
2025-07-02 05:52:56.895 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:56.904
2025-07-02 05:52:56.911 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:56.917 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:56.923
2025-07-02 05:52:56.929 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:56.936 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:56.943 alo = 474, ahi = 1101
2025-07-02 05:52:56.951 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:56.958 blo = 474, bhi = 1101
2025-07-02 05:52:56.965
2025-07-02 05:52:56.970 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:56.976 g = []
2025-07-02 05:52:56.983 if alo < ahi:
2025-07-02 05:52:56.991 if blo < bhi:
2025-07-02 05:52:57.002 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:57.011 else:
2025-07-02 05:52:57.022 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:57.036 elif blo < bhi:
2025-07-02 05:52:57.045 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:57.054
2025-07-02 05:52:57.061 >       yield from g
2025-07-02 05:52:57.074
2025-07-02 05:52:57.084 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:57.093 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:57.100
2025-07-02 05:52:57.105 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:57.111 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:57.117 alo = 474, ahi = 1101
2025-07-02 05:52:57.125 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:57.130 blo = 474, bhi = 1101
2025-07-02 05:52:57.137
2025-07-02 05:52:57.145 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:57.152 r"""
2025-07-02 05:52:57.166 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:57.174 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:57.182 synch point, and intraline difference marking is done on the
2025-07-02 05:52:57.193 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:57.203
2025-07-02 05:52:57.211 Example:
2025-07-02 05:52:57.220
2025-07-02 05:52:57.231 >>> d = Differ()
2025-07-02 05:52:57.242 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:57.250 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:57.264 >>> print(''.join(results), end="")
2025-07-02 05:52:57.275 - abcDefghiJkl
2025-07-02 05:52:57.293 + abcdefGhijkl
2025-07-02 05:52:57.305 """
2025-07-02 05:52:57.311
2025-07-02 05:52:57.317 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:57.323 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:57.330 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:57.336 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:57.346 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:57.357
2025-07-02 05:52:57.367 # search for the pair that matches best without being identical
2025-07-02 05:52:57.375 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:57.384 # on junk -- unless we have to)
2025-07-02 05:52:57.394 for j in range(blo, bhi):
2025-07-02 05:52:57.406 bj = b[j]
2025-07-02 05:52:57.415 cruncher.set_seq2(bj)
2025-07-02 05:52:57.423 for i in range(alo, ahi):
2025-07-02 05:52:57.436 ai = a[i]
2025-07-02 05:52:57.446 if ai == bj:
2025-07-02 05:52:57.455 if eqi is None:
2025-07-02 05:52:57.463 eqi, eqj = i, j
2025-07-02 05:52:57.476 continue
2025-07-02 05:52:57.488 cruncher.set_seq1(ai)
2025-07-02 05:52:57.496 # computing similarity is expensive, so use the quick
2025-07-02 05:52:57.504 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:57.511 # compares by a factor of 3.
2025-07-02 05:52:57.517 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:57.523 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:57.528 # of the computation is cached by cruncher
2025-07-02 05:52:57.534 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:57.541 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:57.546 cruncher.ratio() > best_ratio:
2025-07-02 05:52:57.550 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:57.555 if best_ratio < cutoff:
2025-07-02 05:52:57.560 # no non-identical "pretty close" pair
2025-07-02 05:52:57.564 if eqi is None:
2025-07-02 05:52:57.569 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:57.574 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:57.579 return
2025-07-02 05:52:57.584 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:57.588 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:57.593 else:
2025-07-02 05:52:57.598 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:57.602 eqi = None
2025-07-02 05:52:57.607
2025-07-02 05:52:57.612 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:57.617 # identical
2025-07-02 05:52:57.622
2025-07-02 05:52:57.627 # pump out diffs from before the synch point
2025-07-02 05:52:57.632 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:57.636
2025-07-02 05:52:57.641 # do intraline marking on the synch pair
2025-07-02 05:52:57.646 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:57.654 if eqi is None:
2025-07-02 05:52:57.667 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:57.679 atags = btags = ""
2025-07-02 05:52:57.688 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:57.696 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:57.703 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:57.709 if tag == 'replace':
2025-07-02 05:52:57.714 atags += '^' * la
2025-07-02 05:52:57.721 btags += '^' * lb
2025-07-02 05:52:57.726 elif tag == 'delete':
2025-07-02 05:52:57.733 atags += '-' * la
2025-07-02 05:52:57.738 elif tag == 'insert':
2025-07-02 05:52:57.743 btags += '+' * lb
2025-07-02 05:52:57.748 elif tag == 'equal':
2025-07-02 05:52:57.753 atags += ' ' * la
2025-07-02 05:52:57.764 btags += ' ' * lb
2025-07-02 05:52:57.773 else:
2025-07-02 05:52:57.780 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:57.789 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:57.801 else:
2025-07-02 05:52:57.812 # the synch pair is identical
2025-07-02 05:52:57.821 yield '  ' + aelt
2025-07-02 05:52:57.829
2025-07-02 05:52:57.836 # pump out diffs from after the synch point
2025-07-02 05:52:57.842 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:57.853
2025-07-02 05:52:57.864 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:57.873 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:57.879
2025-07-02 05:52:57.885 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:57.897 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:57.906 alo = 475, ahi = 1101
2025-07-02 05:52:57.916 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:57.923 blo = 475, bhi = 1101
2025-07-02 05:52:57.931
2025-07-02 05:52:57.942 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:57.950 g = []
2025-07-02 05:52:57.958 if alo < ahi:
2025-07-02 05:52:57.968 if blo < bhi:
2025-07-02 05:52:57.978 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:57.988 else:
2025-07-02 05:52:58.000 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:58.013 elif blo < bhi:
2025-07-02 05:52:58.023 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:58.032
2025-07-02 05:52:58.040 >       yield from g
2025-07-02 05:52:58.056
2025-07-02 05:52:58.065 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:58.073 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:58.079
2025-07-02 05:52:58.086 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:58.100 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:58.111 alo = 475, ahi = 1101
2025-07-02 05:52:58.122 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:58.133 blo = 475, bhi = 1101
2025-07-02 05:52:58.143
2025-07-02 05:52:58.151 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:58.164 r"""
2025-07-02 05:52:58.172 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:58.180 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:58.187 synch point, and intraline difference marking is done on the
2025-07-02 05:52:58.194 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:58.199
2025-07-02 05:52:58.207 Example:
2025-07-02 05:52:58.219
2025-07-02 05:52:58.227 >>> d = Differ()
2025-07-02 05:52:58.235 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:58.243 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:58.249 >>> print(''.join(results), end="")
2025-07-02 05:52:58.254 - abcDefghiJkl
2025-07-02 05:52:58.263 + abcdefGhijkl
2025-07-02 05:52:58.273 """
2025-07-02 05:52:58.278
2025-07-02 05:52:58.284 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:58.291 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:58.299 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:58.312 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:58.320 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:58.328
2025-07-02 05:52:58.334 # search for the pair that matches best without being identical
2025-07-02 05:52:58.341 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:58.347 # on junk -- unless we have to)
2025-07-02 05:52:58.358 for j in range(blo, bhi):
2025-07-02 05:52:58.367 bj = b[j]
2025-07-02 05:52:58.374 cruncher.set_seq2(bj)
2025-07-02 05:52:58.379 for i in range(alo, ahi):
2025-07-02 05:52:58.384 ai = a[i]
2025-07-02 05:52:58.389 if ai == bj:
2025-07-02 05:52:58.393 if eqi is None:
2025-07-02 05:52:58.398 eqi, eqj = i, j
2025-07-02 05:52:58.402 continue
2025-07-02 05:52:58.407 cruncher.set_seq1(ai)
2025-07-02 05:52:58.411 # computing similarity is expensive, so use the quick
2025-07-02 05:52:58.416 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:58.420 # compares by a factor of 3.
2025-07-02 05:52:58.425 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:58.429 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:58.435 # of the computation is cached by cruncher
2025-07-02 05:52:58.441 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:58.445 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:58.450 cruncher.ratio() > best_ratio:
2025-07-02 05:52:58.455 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:58.461 if best_ratio < cutoff:
2025-07-02 05:52:58.470 # no non-identical "pretty close" pair
2025-07-02 05:52:58.484 if eqi is None:
2025-07-02 05:52:58.494 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:58.507 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:58.518 return
2025-07-02 05:52:58.531 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:58.541 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:58.549 else:
2025-07-02 05:52:58.556 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:58.563 eqi = None
2025-07-02 05:52:58.577
2025-07-02 05:52:58.589 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:58.599 # identical
2025-07-02 05:52:58.609
2025-07-02 05:52:58.620 # pump out diffs from before the synch point
2025-07-02 05:52:58.631 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:58.640
2025-07-02 05:52:58.655 # do intraline marking on the synch pair
2025-07-02 05:52:58.665 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:58.674 if eqi is None:
2025-07-02 05:52:58.687 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:58.698 atags = btags = ""
2025-07-02 05:52:58.707 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:58.716 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:58.724 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:58.737 if tag == 'replace':
2025-07-02 05:52:58.747 atags += '^' * la
2025-07-02 05:52:58.756 btags += '^' * lb
2025-07-02 05:52:58.762 elif tag == 'delete':
2025-07-02 05:52:58.777 atags += '-' * la
2025-07-02 05:52:58.787 elif tag == 'insert':
2025-07-02 05:52:58.801 btags += '+' * lb
2025-07-02 05:52:58.812 elif tag == 'equal':
2025-07-02 05:52:58.821 atags += ' ' * la
2025-07-02 05:52:58.829 btags += ' ' * lb
2025-07-02 05:52:58.836 else:
2025-07-02 05:52:58.842 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:52:58.849 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:52:58.864 else:
2025-07-02 05:52:58.876 # the synch pair is identical
2025-07-02 05:52:58.885 yield '  ' + aelt
2025-07-02 05:52:58.893
2025-07-02 05:52:58.903 # pump out diffs from after the synch point
2025-07-02 05:52:58.914 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:52:58.923
2025-07-02 05:52:58.931 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:52:58.943 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:58.957
2025-07-02 05:52:58.970 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:58.983 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:58.992 alo = 476, ahi = 1101
2025-07-02 05:52:59.008 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:59.018 blo = 476, bhi = 1101
2025-07-02 05:52:59.029
2025-07-02 05:52:59.040 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:59.052 g = []
2025-07-02 05:52:59.063 if alo < ahi:
2025-07-02 05:52:59.072 if blo < bhi:
2025-07-02 05:52:59.080 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:59.088 else:
2025-07-02 05:52:59.097 g = self._dump('-', a, alo, ahi)
2025-07-02 05:52:59.110 elif blo < bhi:
2025-07-02 05:52:59.123 g = self._dump('+', b, blo, bhi)
2025-07-02 05:52:59.134
2025-07-02 05:52:59.145 >       yield from g
2025-07-02 05:52:59.154
2025-07-02 05:52:59.169 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:52:59.182 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:52:59.192
2025-07-02 05:52:59.202 self = <difflib.Differ object at [hex]>
2025-07-02 05:52:59.214 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:52:59.227 alo = 476, ahi = 1101
2025-07-02 05:52:59.240 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:52:59.248 blo = 476, bhi = 1101
2025-07-02 05:52:59.254
2025-07-02 05:52:59.265 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:52:59.275 r"""
2025-07-02 05:52:59.284 When replacing one block of lines with another, search the blocks
2025-07-02 05:52:59.292 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:52:59.302 synch point, and intraline difference marking is done on the
2025-07-02 05:52:59.314 similar pair. Lots of work, but often worth it.
2025-07-02 05:52:59.323
2025-07-02 05:52:59.335 Example:
2025-07-02 05:52:59.345
2025-07-02 05:52:59.355 >>> d = Differ()
2025-07-02 05:52:59.366 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:52:59.375 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:52:59.383 >>> print(''.join(results), end="")
2025-07-02 05:52:59.394 - abcDefghiJkl
2025-07-02 05:52:59.416 + abcdefGhijkl
2025-07-02 05:52:59.436 """
2025-07-02 05:52:59.444
2025-07-02 05:52:59.458 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:52:59.471 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:52:59.482 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:52:59.494 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:52:59.505 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:52:59.512
2025-07-02 05:52:59.521 # search for the pair that matches best without being identical
2025-07-02 05:52:59.529 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:52:59.536 # on junk -- unless we have to)
2025-07-02 05:52:59.549 for j in range(blo, bhi):
2025-07-02 05:52:59.561 bj = b[j]
2025-07-02 05:52:59.571 cruncher.set_seq2(bj)
2025-07-02 05:52:59.580 for i in range(alo, ahi):
2025-07-02 05:52:59.590 ai = a[i]
2025-07-02 05:52:59.601 if ai == bj:
2025-07-02 05:52:59.611 if eqi is None:
2025-07-02 05:52:59.621 eqi, eqj = i, j
2025-07-02 05:52:59.633 continue
2025-07-02 05:52:59.644 cruncher.set_seq1(ai)
2025-07-02 05:52:59.657 # computing similarity is expensive, so use the quick
2025-07-02 05:52:59.666 # upper bounds first -- have seen this speed up messy
2025-07-02 05:52:59.672 # compares by a factor of 3.
2025-07-02 05:52:59.679 # note that ratio() is only expensive to compute the first
2025-07-02 05:52:59.687 # time it's called on a sequence pair; the expensive part
2025-07-02 05:52:59.698 # of the computation is cached by cruncher
2025-07-02 05:52:59.706 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:52:59.714 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:52:59.722 cruncher.ratio() > best_ratio:
2025-07-02 05:52:59.729 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:52:59.735 if best_ratio < cutoff:
2025-07-02 05:52:59.742 # no non-identical "pretty close" pair
2025-07-02 05:52:59.749 if eqi is None:
2025-07-02 05:52:59.759 # no identical pair either -- treat it as a straight replace
2025-07-02 05:52:59.766 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:52:59.772 return
2025-07-02 05:52:59.779 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:52:59.785 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:52:59.792 else:
2025-07-02 05:52:59.799 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:52:59.806 eqi = None
2025-07-02 05:52:59.814
2025-07-02 05:52:59.822 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:52:59.830 # identical
2025-07-02 05:52:59.838
2025-07-02 05:52:59.845 # pump out diffs from before the synch point
2025-07-02 05:52:59.851 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:52:59.857
2025-07-02 05:52:59.867 # do intraline marking on the synch pair
2025-07-02 05:52:59.880 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:52:59.888 if eqi is None:
2025-07-02 05:52:59.899 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:52:59.911 atags = btags = ""
2025-07-02 05:52:59.919 cruncher.set_seqs(aelt, belt)
2025-07-02 05:52:59.924 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:52:59.930 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:52:59.934 if tag == 'replace':
2025-07-02 05:52:59.940 atags += '^' * la
2025-07-02 05:52:59.945 btags += '^' * lb
2025-07-02 05:52:59.950 elif tag == 'delete':
2025-07-02 05:52:59.956 atags += '-' * la
2025-07-02 05:52:59.962 elif tag == 'insert':
2025-07-02 05:52:59.969 btags += '+' * lb
2025-07-02 05:52:59.975 elif tag == 'equal':
2025-07-02 05:52:59.982 atags += ' ' * la
2025-07-02 05:52:59.989 btags += ' ' * lb
2025-07-02 05:52:59.999 else:
2025-07-02 05:53:00.010 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:00.020 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:00.028 else:
2025-07-02 05:53:00.036 # the synch pair is identical
2025-07-02 05:53:00.042 yield '  ' + aelt
2025-07-02 05:53:00.049
2025-07-02 05:53:00.055 # pump out diffs from after the synch point
2025-07-02 05:53:00.062 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:00.068
2025-07-02 05:53:00.075 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:00.088 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:00.100
2025-07-02 05:53:00.112 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:00.122 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:00.129 alo = 477, ahi = 1101
2025-07-02 05:53:00.137 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:00.144 blo = 477, bhi = 1101
2025-07-02 05:53:00.150
2025-07-02 05:53:00.156 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:00.161 g = []
2025-07-02 05:53:00.166 if alo < ahi:
2025-07-02 05:53:00.171 if blo < bhi:
2025-07-02 05:53:00.176 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:00.181 else:
2025-07-02 05:53:00.187 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:00.193 elif blo < bhi:
2025-07-02 05:53:00.199 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:00.207
2025-07-02 05:53:00.217 >       yield from g
2025-07-02 05:53:00.226
2025-07-02 05:53:00.236 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:00.247 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:00.258
2025-07-02 05:53:00.270 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:00.280 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:00.287 alo = 477, ahi = 1101
2025-07-02 05:53:00.295 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:00.301 blo = 477, bhi = 1101
2025-07-02 05:53:00.307
2025-07-02 05:53:00.315 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:00.326 r"""
2025-07-02 05:53:00.337 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:00.345 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:00.351 synch point, and intraline difference marking is done on the
2025-07-02 05:53:00.358 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:00.363
2025-07-02 05:53:00.368 Example:
2025-07-02 05:53:00.373
2025-07-02 05:53:00.378 >>> d = Differ()
2025-07-02 05:53:00.384 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:00.390 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:00.396 >>> print(''.join(results), end="")
2025-07-02 05:53:00.402 - abcDefghiJkl
2025-07-02 05:53:00.413 + abcdefGhijkl
2025-07-02 05:53:00.425 """
2025-07-02 05:53:00.431
2025-07-02 05:53:00.438 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:00.446 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:00.451 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:00.457 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:00.462 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:00.467
2025-07-02 05:53:00.472 # search for the pair that matches best without being identical
2025-07-02 05:53:00.477 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:00.482 # on junk -- unless we have to)
2025-07-02 05:53:00.487 for j in range(blo, bhi):
2025-07-02 05:53:00.493 bj = b[j]
2025-07-02 05:53:00.499 cruncher.set_seq2(bj)
2025-07-02 05:53:00.505 for i in range(alo, ahi):
2025-07-02 05:53:00.511 ai = a[i]
2025-07-02 05:53:00.520 if ai == bj:
2025-07-02 05:53:00.530 if eqi is None:
2025-07-02 05:53:00.540 eqi, eqj = i, j
2025-07-02 05:53:00.548 continue
2025-07-02 05:53:00.556 cruncher.set_seq1(ai)
2025-07-02 05:53:00.563 # computing similarity is expensive, so use the quick
2025-07-02 05:53:00.570 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:00.576 # compares by a factor of 3.
2025-07-02 05:53:00.582 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:00.588 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:00.594 # of the computation is cached by cruncher
2025-07-02 05:53:00.601 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:00.607 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:00.615 cruncher.ratio() > best_ratio:
2025-07-02 05:53:00.626 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:00.634 if best_ratio < cutoff:
2025-07-02 05:53:00.641 # no non-identical "pretty close" pair
2025-07-02 05:53:00.648 if eqi is None:
2025-07-02 05:53:00.655 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:00.661 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:00.667 return
2025-07-02 05:53:00.674 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:00.683 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:00.693 else:
2025-07-02 05:53:00.701 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:00.708 eqi = None
2025-07-02 05:53:00.715
2025-07-02 05:53:00.722 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:00.733 # identical
2025-07-02 05:53:00.743
2025-07-02 05:53:00.752 # pump out diffs from before the synch point
2025-07-02 05:53:00.760 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:00.767
2025-07-02 05:53:00.776 # do intraline marking on the synch pair
2025-07-02 05:53:00.785 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:00.790 if eqi is None:
2025-07-02 05:53:00.796 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:00.802 atags = btags = ""
2025-07-02 05:53:00.810 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:00.822 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:00.834 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:00.846 if tag == 'replace':
2025-07-02 05:53:00.858 atags += '^' * la
2025-07-02 05:53:00.868 btags += '^' * lb
2025-07-02 05:53:00.876 elif tag == 'delete':
2025-07-02 05:53:00.887 atags += '-' * la
2025-07-02 05:53:00.895 elif tag == 'insert':
2025-07-02 05:53:00.904 btags += '+' * lb
2025-07-02 05:53:00.911 elif tag == 'equal':
2025-07-02 05:53:00.919 atags += ' ' * la
2025-07-02 05:53:00.930 btags += ' ' * lb
2025-07-02 05:53:00.941 else:
2025-07-02 05:53:00.949 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:00.959 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:00.967 else:
2025-07-02 05:53:00.975 # the synch pair is identical
2025-07-02 05:53:00.981 yield '  ' + aelt
2025-07-02 05:53:00.988
2025-07-02 05:53:00.994 # pump out diffs from after the synch point
2025-07-02 05:53:01.001 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:01.007
2025-07-02 05:53:01.013 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:01.019 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:01.029
2025-07-02 05:53:01.040 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:01.052 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:01.062 alo = 478, ahi = 1101
2025-07-02 05:53:01.069 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:01.075 blo = 478, bhi = 1101
2025-07-02 05:53:01.081
2025-07-02 05:53:01.088 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:01.095 g = []
2025-07-02 05:53:01.101 if alo < ahi:
2025-07-02 05:53:01.111 if blo < bhi:
2025-07-02 05:53:01.117 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:01.126 else:
2025-07-02 05:53:01.135 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:01.143 elif blo < bhi:
2025-07-02 05:53:01.151 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:01.157
2025-07-02 05:53:01.162 >       yield from g
2025-07-02 05:53:01.171
2025-07-02 05:53:01.178 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:01.185 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:01.191
2025-07-02 05:53:01.199 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:01.211 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:01.220 alo = 478, ahi = 1101
2025-07-02 05:53:01.228 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:01.235 blo = 478, bhi = 1101
2025-07-02 05:53:01.240
2025-07-02 05:53:01.245 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:01.254 r"""
2025-07-02 05:53:01.266 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:01.277 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:01.287 synch point, and intraline difference marking is done on the
2025-07-02 05:53:01.296 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:01.306
2025-07-02 05:53:01.316 Example:
2025-07-02 05:53:01.323
2025-07-02 05:53:01.331 >>> d = Differ()
2025-07-02 05:53:01.338 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:01.346 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:01.353 >>> print(''.join(results), end="")
2025-07-02 05:53:01.365 - abcDefghiJkl
2025-07-02 05:53:01.385 + abcdefGhijkl
2025-07-02 05:53:01.401 """
2025-07-02 05:53:01.414
2025-07-02 05:53:01.425 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:01.433 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:01.440 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:01.448 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:01.461 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:01.471
2025-07-02 05:53:01.486 # search for the pair that matches best without being identical
2025-07-02 05:53:01.498 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:01.506 # on junk -- unless we have to)
2025-07-02 05:53:01.512 for j in range(blo, bhi):
2025-07-02 05:53:01.518 bj = b[j]
2025-07-02 05:53:01.524 cruncher.set_seq2(bj)
2025-07-02 05:53:01.530 for i in range(alo, ahi):
2025-07-02 05:53:01.542 ai = a[i]
2025-07-02 05:53:01.554 if ai == bj:
2025-07-02 05:53:01.563 if eqi is None:
2025-07-02 05:53:01.572 eqi, eqj = i, j
2025-07-02 05:53:01.579 continue
2025-07-02 05:53:01.585 cruncher.set_seq1(ai)
2025-07-02 05:53:01.591 # computing similarity is expensive, so use the quick
2025-07-02 05:53:01.598 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:01.604 # compares by a factor of 3.
2025-07-02 05:53:01.612 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:01.619 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:01.625 # of the computation is cached by cruncher
2025-07-02 05:53:01.638 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:01.649 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:01.659 cruncher.ratio() > best_ratio:
2025-07-02 05:53:01.667 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:01.680 if best_ratio < cutoff:
2025-07-02 05:53:01.690 # no non-identical "pretty close" pair
2025-07-02 05:53:01.704 if eqi is None:
2025-07-02 05:53:01.717 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:01.730 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:01.741 return
2025-07-02 05:53:01.749 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:01.761 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:01.772 else:
2025-07-02 05:53:01.784 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:01.796 eqi = None
2025-07-02 05:53:01.806
2025-07-02 05:53:01.812 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:01.818 # identical
2025-07-02 05:53:01.825
2025-07-02 05:53:01.831 # pump out diffs from before the synch point
2025-07-02 05:53:01.838 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:01.844
2025-07-02 05:53:01.850 # do intraline marking on the synch pair
2025-07-02 05:53:01.856 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:01.862 if eqi is None:
2025-07-02 05:53:01.869 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:01.874 atags = btags = ""
2025-07-02 05:53:01.880 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:01.886 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:01.897 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:01.906 if tag == 'replace':
2025-07-02 05:53:01.914 atags += '^' * la
2025-07-02 05:53:01.922 btags += '^' * lb
2025-07-02 05:53:01.930 elif tag == 'delete':
2025-07-02 05:53:01.941 atags += '-' * la
2025-07-02 05:53:01.949 elif tag == 'insert':
2025-07-02 05:53:01.957 btags += '+' * lb
2025-07-02 05:53:01.968 elif tag == 'equal':
2025-07-02 05:53:01.978 atags += ' ' * la
2025-07-02 05:53:01.986 btags += ' ' * lb
2025-07-02 05:53:01.995 else:
2025-07-02 05:53:02.003 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:02.009 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:02.015 else:
2025-07-02 05:53:02.020 # the synch pair is identical
2025-07-02 05:53:02.029 yield '  ' + aelt
2025-07-02 05:53:02.038
2025-07-02 05:53:02.047 # pump out diffs from after the synch point
2025-07-02 05:53:02.056 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:02.067
2025-07-02 05:53:02.078 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:02.089 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:02.096
2025-07-02 05:53:02.102 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:02.111 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:02.118 alo = 479, ahi = 1101
2025-07-02 05:53:02.126 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:02.131 blo = 479, bhi = 1101
2025-07-02 05:53:02.135
2025-07-02 05:53:02.140 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:02.145 g = []
2025-07-02 05:53:02.149 if alo < ahi:
2025-07-02 05:53:02.154 if blo < bhi:
2025-07-02 05:53:02.167 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:02.177 else:
2025-07-02 05:53:02.187 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:02.194 elif blo < bhi:
2025-07-02 05:53:02.202 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:02.214
2025-07-02 05:53:02.222 >       yield from g
2025-07-02 05:53:02.229
2025-07-02 05:53:02.236 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:02.242 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:02.248
2025-07-02 05:53:02.254 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:02.261 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:02.266 alo = 479, ahi = 1101
2025-07-02 05:53:02.272 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:02.277 blo = 479, bhi = 1101
2025-07-02 05:53:02.281
2025-07-02 05:53:02.287 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:02.292 r"""
2025-07-02 05:53:02.297 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:02.302 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:02.308 synch point, and intraline difference marking is done on the
2025-07-02 05:53:02.314 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:02.320
2025-07-02 05:53:02.325 Example:
2025-07-02 05:53:02.331
2025-07-02 05:53:02.337 >>> d = Differ()
2025-07-02 05:53:02.344 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:02.350 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:02.356 >>> print(''.join(results), end="")
2025-07-02 05:53:02.364 - abcDefghiJkl
2025-07-02 05:53:02.379 + abcdefGhijkl
2025-07-02 05:53:02.392 """
2025-07-02 05:53:02.398
2025-07-02 05:53:02.405 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:02.413 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:02.420 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:02.428 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:02.441 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:02.448
2025-07-02 05:53:02.459 # search for the pair that matches best without being identical
2025-07-02 05:53:02.471 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:02.479 # on junk -- unless we have to)
2025-07-02 05:53:02.489 for j in range(blo, bhi):
2025-07-02 05:53:02.496 bj = b[j]
2025-07-02 05:53:02.511 cruncher.set_seq2(bj)
2025-07-02 05:53:02.521 for i in range(alo, ahi):
2025-07-02 05:53:02.529 ai = a[i]
2025-07-02 05:53:02.536 if ai == bj:
2025-07-02 05:53:02.542 if eqi is None:
2025-07-02 05:53:02.550 eqi, eqj = i, j
2025-07-02 05:53:02.559 continue
2025-07-02 05:53:02.567 cruncher.set_seq1(ai)
2025-07-02 05:53:02.575 # computing similarity is expensive, so use the quick
2025-07-02 05:53:02.585 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:02.593 # compares by a factor of 3.
2025-07-02 05:53:02.601 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:02.608 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:02.615 # of the computation is cached by cruncher
2025-07-02 05:53:02.622 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:02.630 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:02.641 cruncher.ratio() > best_ratio:
2025-07-02 05:53:02.650 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:02.659 if best_ratio < cutoff:
2025-07-02 05:53:02.667 # no non-identical "pretty close" pair
2025-07-02 05:53:02.677 if eqi is None:
2025-07-02 05:53:02.686 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:02.695 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:02.704 return
2025-07-02 05:53:02.711 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:02.718 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:02.731 else:
2025-07-02 05:53:02.745 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:02.755 eqi = None
2025-07-02 05:53:02.762
2025-07-02 05:53:02.770 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:02.777 # identical
2025-07-02 05:53:02.784
2025-07-02 05:53:02.792 # pump out diffs from before the synch point
2025-07-02 05:53:02.798 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:02.805
2025-07-02 05:53:02.813 # do intraline marking on the synch pair
2025-07-02 05:53:02.820 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:02.828 if eqi is None:
2025-07-02 05:53:02.837 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:02.844 atags = btags = ""
2025-07-02 05:53:02.851 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:02.858 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:02.864 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:02.876 if tag == 'replace':
2025-07-02 05:53:02.889 atags += '^' * la
2025-07-02 05:53:02.901 btags += '^' * lb
2025-07-02 05:53:02.910 elif tag == 'delete':
2025-07-02 05:53:02.918 atags += '-' * la
2025-07-02 05:53:02.925 elif tag == 'insert':
2025-07-02 05:53:02.934 btags += '+' * lb
2025-07-02 05:53:02.945 elif tag == 'equal':
2025-07-02 05:53:02.956 atags += ' ' * la
2025-07-02 05:53:02.969 btags += ' ' * lb
2025-07-02 05:53:02.980 else:
2025-07-02 05:53:02.992 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:03.006 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:03.018 else:
2025-07-02 05:53:03.029 # the synch pair is identical
2025-07-02 05:53:03.041 yield '  ' + aelt
2025-07-02 05:53:03.051
2025-07-02 05:53:03.059 # pump out diffs from after the synch point
2025-07-02 05:53:03.067 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:03.075
2025-07-02 05:53:03.086 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:03.094 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:03.101
2025-07-02 05:53:03.114 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:03.122 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:03.134 alo = 480, ahi = 1101
2025-07-02 05:53:03.147 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:03.162 blo = 480, bhi = 1101
2025-07-02 05:53:03.173
2025-07-02 05:53:03.180 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:03.186 g = []
2025-07-02 05:53:03.191 if alo < ahi:
2025-07-02 05:53:03.196 if blo < bhi:
2025-07-02 05:53:03.202 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:03.207 else:
2025-07-02 05:53:03.214 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:03.220 elif blo < bhi:
2025-07-02 05:53:03.226 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:03.232
2025-07-02 05:53:03.238 >       yield from g
2025-07-02 05:53:03.244
2025-07-02 05:53:03.250 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:03.257 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:03.264
2025-07-02 05:53:03.271 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:03.280 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:03.288 alo = 480, ahi = 1101
2025-07-02 05:53:03.301 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:03.309 blo = 480, bhi = 1101
2025-07-02 05:53:03.315
2025-07-02 05:53:03.321 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:03.328 r"""
2025-07-02 05:53:03.338 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:03.354 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:03.366 synch point, and intraline difference marking is done on the
2025-07-02 05:53:03.373 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:03.379
2025-07-02 05:53:03.386 Example:
2025-07-02 05:53:03.393
2025-07-02 05:53:03.400 >>> d = Differ()
2025-07-02 05:53:03.406 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:03.412 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:03.419 >>> print(''.join(results), end="")
2025-07-02 05:53:03.425 - abcDefghiJkl
2025-07-02 05:53:03.438 + abcdefGhijkl
2025-07-02 05:53:03.450 """
2025-07-02 05:53:03.456
2025-07-02 05:53:03.463 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:03.470 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:03.481 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:03.491 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:03.499 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:03.507
2025-07-02 05:53:03.519 # search for the pair that matches best without being identical
2025-07-02 05:53:03.529 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:03.540 # on junk -- unless we have to)
2025-07-02 05:53:03.548 for j in range(blo, bhi):
2025-07-02 05:53:03.555 bj = b[j]
2025-07-02 05:53:03.563 cruncher.set_seq2(bj)
2025-07-02 05:53:03.574 for i in range(alo, ahi):
2025-07-02 05:53:03.582 ai = a[i]
2025-07-02 05:53:03.591 if ai == bj:
2025-07-02 05:53:03.599 if eqi is None:
2025-07-02 05:53:03.606 eqi, eqj = i, j
2025-07-02 05:53:03.617 continue
2025-07-02 05:53:03.627 cruncher.set_seq1(ai)
2025-07-02 05:53:03.635 # computing similarity is expensive, so use the quick
2025-07-02 05:53:03.642 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:03.653 # compares by a factor of 3.
2025-07-02 05:53:03.664 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:03.676 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:03.686 # of the computation is cached by cruncher
2025-07-02 05:53:03.693 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:03.699 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:03.705 cruncher.ratio() > best_ratio:
2025-07-02 05:53:03.710 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:03.721 if best_ratio < cutoff:
2025-07-02 05:53:03.730 # no non-identical "pretty close" pair
2025-07-02 05:53:03.737 if eqi is None:
2025-07-02 05:53:03.749 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:03.760 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:03.769 return
2025-07-02 05:53:03.780 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:03.789 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:03.798 else:
2025-07-02 05:53:03.806 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:03.814 eqi = None
2025-07-02 05:53:03.823
2025-07-02 05:53:03.835 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:03.846 # identical
2025-07-02 05:53:03.857
2025-07-02 05:53:03.868 # pump out diffs from before the synch point
2025-07-02 05:53:03.876 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:03.882
2025-07-02 05:53:03.889 # do intraline marking on the synch pair
2025-07-02 05:53:03.894 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:03.900 if eqi is None:
2025-07-02 05:53:03.905 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:03.910 atags = btags = ""
2025-07-02 05:53:03.915 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:03.920 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:03.925 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:03.930 if tag == 'replace':
2025-07-02 05:53:03.935 atags += '^' * la
2025-07-02 05:53:03.944 btags += '^' * lb
2025-07-02 05:53:03.957 elif tag == 'delete':
2025-07-02 05:53:03.967 atags += '-' * la
2025-07-02 05:53:03.975 elif tag == 'insert':
2025-07-02 05:53:03.985 btags += '+' * lb
2025-07-02 05:53:03.992 elif tag == 'equal':
2025-07-02 05:53:04.000 atags += ' ' * la
2025-07-02 05:53:04.009 btags += ' ' * lb
2025-07-02 05:53:04.015 else:
2025-07-02 05:53:04.023 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:04.032 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:04.040 else:
2025-07-02 05:53:04.053 # the synch pair is identical
2025-07-02 05:53:04.068 yield '  ' + aelt
2025-07-02 05:53:04.080
2025-07-02 05:53:04.092 # pump out diffs from after the synch point
2025-07-02 05:53:04.101 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:04.113
2025-07-02 05:53:04.124 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:04.135 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:04.148
2025-07-02 05:53:04.162 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:04.173 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:04.186 alo = 481, ahi = 1101
2025-07-02 05:53:04.200 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:04.214 blo = 481, bhi = 1101
2025-07-02 05:53:04.228
2025-07-02 05:53:04.238 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:04.248 g = []
2025-07-02 05:53:04.256 if alo < ahi:
2025-07-02 05:53:04.267 if blo < bhi:
2025-07-02 05:53:04.281 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:04.293 else:
2025-07-02 05:53:04.302 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:04.311 elif blo < bhi:
2025-07-02 05:53:04.318 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:04.325
2025-07-02 05:53:04.332 >       yield from g
2025-07-02 05:53:04.338
2025-07-02 05:53:04.353 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:04.364 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:04.373
2025-07-02 05:53:04.381 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:04.391 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:04.402 alo = 481, ahi = 1101
2025-07-02 05:53:04.417 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:04.425 blo = 481, bhi = 1101
2025-07-02 05:53:04.432
2025-07-02 05:53:04.440 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:04.446 r"""
2025-07-02 05:53:04.452 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:04.459 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:04.466 synch point, and intraline difference marking is done on the
2025-07-02 05:53:04.471 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:04.477
2025-07-02 05:53:04.482 Example:
2025-07-02 05:53:04.494
2025-07-02 05:53:04.505 >>> d = Differ()
2025-07-02 05:53:04.515 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:04.527 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:04.537 >>> print(''.join(results), end="")
2025-07-02 05:53:04.547 - abcDefghiJkl
2025-07-02 05:53:04.563 + abcdefGhijkl
2025-07-02 05:53:04.575 """
2025-07-02 05:53:04.583
2025-07-02 05:53:04.595 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:04.604 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:04.610 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:04.620 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:04.628 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:04.635
2025-07-02 05:53:04.642 # search for the pair that matches best without being identical
2025-07-02 05:53:04.648 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:04.654 # on junk -- unless we have to)
2025-07-02 05:53:04.660 for j in range(blo, bhi):
2025-07-02 05:53:04.667 bj = b[j]
2025-07-02 05:53:04.673 cruncher.set_seq2(bj)
2025-07-02 05:53:04.679 for i in range(alo, ahi):
2025-07-02 05:53:04.688 ai = a[i]
2025-07-02 05:53:04.700 if ai == bj:
2025-07-02 05:53:04.708 if eqi is None:
2025-07-02 05:53:04.716 eqi, eqj = i, j
2025-07-02 05:53:04.723 continue
2025-07-02 05:53:04.729 cruncher.set_seq1(ai)
2025-07-02 05:53:04.735 # computing similarity is expensive, so use the quick
2025-07-02 05:53:04.742 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:04.752 # compares by a factor of 3.
2025-07-02 05:53:04.764 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:04.772 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:04.778 # of the computation is cached by cruncher
2025-07-02 05:53:04.785 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:04.790 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:04.797 cruncher.ratio() > best_ratio:
2025-07-02 05:53:04.805 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:04.814 if best_ratio < cutoff:
2025-07-02 05:53:04.826 # no non-identical "pretty close" pair
2025-07-02 05:53:04.837 if eqi is None:
2025-07-02 05:53:04.849 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:04.860 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:04.871 return
2025-07-02 05:53:04.882 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:04.896 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:04.906 else:
2025-07-02 05:53:04.917 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:04.924 eqi = None
2025-07-02 05:53:04.933
2025-07-02 05:53:04.942 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:04.950 # identical
2025-07-02 05:53:04.961
2025-07-02 05:53:04.973 # pump out diffs from before the synch point
2025-07-02 05:53:04.981 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:04.988
2025-07-02 05:53:04.997 # do intraline marking on the synch pair
2025-07-02 05:53:05.008 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:05.020 if eqi is None:
2025-07-02 05:53:05.028 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:05.038 atags = btags = ""
2025-07-02 05:53:05.045 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:05.051 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:05.065 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:05.077 if tag == 'replace':
2025-07-02 05:53:05.085 atags += '^' * la
2025-07-02 05:53:05.093 btags += '^' * lb
2025-07-02 05:53:05.099 elif tag == 'delete':
2025-07-02 05:53:05.106 atags += '-' * la
2025-07-02 05:53:05.117 elif tag == 'insert':
2025-07-02 05:53:05.127 btags += '+' * lb
2025-07-02 05:53:05.134 elif tag == 'equal':
2025-07-02 05:53:05.141 atags += ' ' * la
2025-07-02 05:53:05.147 btags += ' ' * lb
2025-07-02 05:53:05.155 else:
2025-07-02 05:53:05.162 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:05.170 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:05.177 else:
2025-07-02 05:53:05.186 # the synch pair is identical
2025-07-02 05:53:05.199 yield '  ' + aelt
2025-07-02 05:53:05.212
2025-07-02 05:53:05.224 # pump out diffs from after the synch point
2025-07-02 05:53:05.232 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:05.239
2025-07-02 05:53:05.247 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:05.254 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:05.261
2025-07-02 05:53:05.268 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:05.278 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:05.285 alo = 482, ahi = 1101
2025-07-02 05:53:05.294 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:05.302 blo = 482, bhi = 1101
2025-07-02 05:53:05.308
2025-07-02 05:53:05.315 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:05.323 g = []
2025-07-02 05:53:05.332 if alo < ahi:
2025-07-02 05:53:05.339 if blo < bhi:
2025-07-02 05:53:05.345 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:05.350 else:
2025-07-02 05:53:05.356 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:05.362 elif blo < bhi:
2025-07-02 05:53:05.377 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:05.387
2025-07-02 05:53:05.400 >       yield from g
2025-07-02 05:53:05.414
2025-07-02 05:53:05.423 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:05.430 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:05.436
2025-07-02 05:53:05.444 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:05.453 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:05.459 alo = 482, ahi = 1101
2025-07-02 05:53:05.473 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:05.479 blo = 482, bhi = 1101
2025-07-02 05:53:05.486
2025-07-02 05:53:05.493 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:05.499 r"""
2025-07-02 05:53:05.506 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:05.512 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:05.519 synch point, and intraline difference marking is done on the
2025-07-02 05:53:05.527 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:05.538
2025-07-02 05:53:05.550 Example:
2025-07-02 05:53:05.560
2025-07-02 05:53:05.568 >>> d = Differ()
2025-07-02 05:53:05.576 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:05.589 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:05.600 >>> print(''.join(results), end="")
2025-07-02 05:53:05.614 - abcDefghiJkl
2025-07-02 05:53:05.636 + abcdefGhijkl
2025-07-02 05:53:05.663 """
2025-07-02 05:53:05.673
2025-07-02 05:53:05.685 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:05.695 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:05.702 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:05.709 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:05.715 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:05.722
2025-07-02 05:53:05.730 # search for the pair that matches best without being identical
2025-07-02 05:53:05.738 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:05.744 # on junk -- unless we have to)
2025-07-02 05:53:05.750 for j in range(blo, bhi):
2025-07-02 05:53:05.755 bj = b[j]
2025-07-02 05:53:05.761 cruncher.set_seq2(bj)
2025-07-02 05:53:05.771 for i in range(alo, ahi):
2025-07-02 05:53:05.778 ai = a[i]
2025-07-02 05:53:05.784 if ai == bj:
2025-07-02 05:53:05.791 if eqi is None:
2025-07-02 05:53:05.798 eqi, eqj = i, j
2025-07-02 05:53:05.804 continue
2025-07-02 05:53:05.810 cruncher.set_seq1(ai)
2025-07-02 05:53:05.816 # computing similarity is expensive, so use the quick
2025-07-02 05:53:05.823 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:05.830 # compares by a factor of 3.
2025-07-02 05:53:05.836 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:05.843 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:05.849 # of the computation is cached by cruncher
2025-07-02 05:53:05.856 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:05.863 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:05.869 cruncher.ratio() > best_ratio:
2025-07-02 05:53:05.875 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:05.883 if best_ratio < cutoff:
2025-07-02 05:53:05.891 # no non-identical "pretty close" pair
2025-07-02 05:53:05.898 if eqi is None:
2025-07-02 05:53:05.904 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:05.910 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:05.922 return
2025-07-02 05:53:05.933 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:05.941 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:05.947 else:
2025-07-02 05:53:05.954 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:05.962 eqi = None
2025-07-02 05:53:05.970
2025-07-02 05:53:05.979 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:05.985 # identical
2025-07-02 05:53:05.991
2025-07-02 05:53:05.997 # pump out diffs from before the synch point
2025-07-02 05:53:06.006 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:06.011
2025-07-02 05:53:06.018 # do intraline marking on the synch pair
2025-07-02 05:53:06.023 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:06.028 if eqi is None:
2025-07-02 05:53:06.040 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:06.050 atags = btags = ""
2025-07-02 05:53:06.066 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:06.077 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:06.084 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:06.090 if tag == 'replace':
2025-07-02 05:53:06.097 atags += '^' * la
2025-07-02 05:53:06.102 btags += '^' * lb
2025-07-02 05:53:06.107 elif tag == 'delete':
2025-07-02 05:53:06.112 atags += '-' * la
2025-07-02 05:53:06.119 elif tag == 'insert':
2025-07-02 05:53:06.125 btags += '+' * lb
2025-07-02 05:53:06.131 elif tag == 'equal':
2025-07-02 05:53:06.136 atags += ' ' * la
2025-07-02 05:53:06.142 btags += ' ' * lb
2025-07-02 05:53:06.148 else:
2025-07-02 05:53:06.156 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:06.163 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:06.169 else:
2025-07-02 05:53:06.175 # the synch pair is identical
2025-07-02 05:53:06.181 yield '  ' + aelt
2025-07-02 05:53:06.187
2025-07-02 05:53:06.193 # pump out diffs from after the synch point
2025-07-02 05:53:06.199 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:06.206
2025-07-02 05:53:06.211 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:06.218 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:06.224
2025-07-02 05:53:06.230 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:06.238 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:06.243 alo = 483, ahi = 1101
2025-07-02 05:53:06.251 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:06.259 blo = 483, bhi = 1101
2025-07-02 05:53:06.270
2025-07-02 05:53:06.279 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:06.289 g = []
2025-07-02 05:53:06.298 if alo < ahi:
2025-07-02 05:53:06.306 if blo < bhi:
2025-07-02 05:53:06.316 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:06.326 else:
2025-07-02 05:53:06.334 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:06.343 elif blo < bhi:
2025-07-02 05:53:06.353 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:06.362
2025-07-02 05:53:06.369 >       yield from g
2025-07-02 05:53:06.375
2025-07-02 05:53:06.381 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:06.393 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:06.401
2025-07-02 05:53:06.407 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:06.414 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:06.421 alo = 483, ahi = 1101
2025-07-02 05:53:06.428 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:06.433 blo = 483, bhi = 1101
2025-07-02 05:53:06.437
2025-07-02 05:53:06.442 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:06.448 r"""
2025-07-02 05:53:06.453 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:06.465 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:06.476 synch point, and intraline difference marking is done on the
2025-07-02 05:53:06.488 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:06.496
2025-07-02 05:53:06.503 Example:
2025-07-02 05:53:06.510
2025-07-02 05:53:06.516 >>> d = Differ()
2025-07-02 05:53:06.522 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:06.528 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:06.537 >>> print(''.join(results), end="")
2025-07-02 05:53:06.544 - abcDefghiJkl
2025-07-02 05:53:06.559 + abcdefGhijkl
2025-07-02 05:53:06.573 """
2025-07-02 05:53:06.583
2025-07-02 05:53:06.590 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:06.599 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:06.608 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:06.617 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:06.624 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:06.630
2025-07-02 05:53:06.637 # search for the pair that matches best without being identical
2025-07-02 05:53:06.644 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:06.651 # on junk -- unless we have to)
2025-07-02 05:53:06.659 for j in range(blo, bhi):
2025-07-02 05:53:06.669 bj = b[j]
2025-07-02 05:53:06.677 cruncher.set_seq2(bj)
2025-07-02 05:53:06.683 for i in range(alo, ahi):
2025-07-02 05:53:06.688 ai = a[i]
2025-07-02 05:53:06.694 if ai == bj:
2025-07-02 05:53:06.703 if eqi is None:
2025-07-02 05:53:06.711 eqi, eqj = i, j
2025-07-02 05:53:06.717 continue
2025-07-02 05:53:06.726 cruncher.set_seq1(ai)
2025-07-02 05:53:06.736 # computing similarity is expensive, so use the quick
2025-07-02 05:53:06.744 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:06.752 # compares by a factor of 3.
2025-07-02 05:53:06.758 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:06.769 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:06.778 # of the computation is cached by cruncher
2025-07-02 05:53:06.787 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:06.794 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:06.806 cruncher.ratio() > best_ratio:
2025-07-02 05:53:06.815 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:06.822 if best_ratio < cutoff:
2025-07-02 05:53:06.833 # no non-identical "pretty close" pair
2025-07-02 05:53:06.842 if eqi is None:
2025-07-02 05:53:06.851 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:06.859 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:06.870 return
2025-07-02 05:53:06.878 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:06.884 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:06.890 else:
2025-07-02 05:53:06.897 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:06.902 eqi = None
2025-07-02 05:53:06.912
2025-07-02 05:53:06.922 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:06.930 # identical
2025-07-02 05:53:06.936
2025-07-02 05:53:06.943 # pump out diffs from before the synch point
2025-07-02 05:53:06.954 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:06.965
2025-07-02 05:53:06.975 # do intraline marking on the synch pair
2025-07-02 05:53:06.986 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:06.996 if eqi is None:
2025-07-02 05:53:07.007 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:07.018 atags = btags = ""
2025-07-02 05:53:07.029 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:07.036 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:07.043 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:07.053 if tag == 'replace':
2025-07-02 05:53:07.064 atags += '^' * la
2025-07-02 05:53:07.074 btags += '^' * lb
2025-07-02 05:53:07.082 elif tag == 'delete':
2025-07-02 05:53:07.091 atags += '-' * la
2025-07-02 05:53:07.098 elif tag == 'insert':
2025-07-02 05:53:07.106 btags += '+' * lb
2025-07-02 05:53:07.117 elif tag == 'equal':
2025-07-02 05:53:07.126 atags += ' ' * la
2025-07-02 05:53:07.134 btags += ' ' * lb
2025-07-02 05:53:07.142 else:
2025-07-02 05:53:07.152 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:07.159 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:07.166 else:
2025-07-02 05:53:07.174 # the synch pair is identical
2025-07-02 05:53:07.185 yield '  ' + aelt
2025-07-02 05:53:07.193
2025-07-02 05:53:07.201 # pump out diffs from after the synch point
2025-07-02 05:53:07.208 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:07.214
2025-07-02 05:53:07.226 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:07.235 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:07.242
2025-07-02 05:53:07.249 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:07.262 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:07.272 alo = 484, ahi = 1101
2025-07-02 05:53:07.279 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:07.290 blo = 484, bhi = 1101
2025-07-02 05:53:07.301
2025-07-02 05:53:07.312 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:07.320 g = []
2025-07-02 05:53:07.333 if alo < ahi:
2025-07-02 05:53:07.343 if blo < bhi:
2025-07-02 05:53:07.352 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:07.362 else:
2025-07-02 05:53:07.371 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:07.384 elif blo < bhi:
2025-07-02 05:53:07.394 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:07.404
2025-07-02 05:53:07.415 >       yield from g
2025-07-02 05:53:07.423
2025-07-02 05:53:07.430 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:07.443 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:07.452
2025-07-02 05:53:07.459 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:07.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:07.473 alo = 484, ahi = 1101
2025-07-02 05:53:07.486 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:07.496 blo = 484, bhi = 1101
2025-07-02 05:53:07.504
2025-07-02 05:53:07.511 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:07.517 r"""
2025-07-02 05:53:07.524 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:07.530 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:07.536 synch point, and intraline difference marking is done on the
2025-07-02 05:53:07.543 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:07.549
2025-07-02 05:53:07.555 Example:
2025-07-02 05:53:07.561
2025-07-02 05:53:07.567 >>> d = Differ()
2025-07-02 05:53:07.581 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:07.595 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:07.605 >>> print(''.join(results), end="")
2025-07-02 05:53:07.616 - abcDefghiJkl
2025-07-02 05:53:07.638 + abcdefGhijkl
2025-07-02 05:53:07.663 """
2025-07-02 05:53:07.671
2025-07-02 05:53:07.678 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:07.687 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:07.699 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:07.713 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:07.725 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:07.735
2025-07-02 05:53:07.743 # search for the pair that matches best without being identical
2025-07-02 05:53:07.751 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:07.757 # on junk -- unless we have to)
2025-07-02 05:53:07.763 for j in range(blo, bhi):
2025-07-02 05:53:07.769 bj = b[j]
2025-07-02 05:53:07.776 cruncher.set_seq2(bj)
2025-07-02 05:53:07.782 for i in range(alo, ahi):
2025-07-02 05:53:07.795 ai = a[i]
2025-07-02 05:53:07.806 if ai == bj:
2025-07-02 05:53:07.815 if eqi is None:
2025-07-02 05:53:07.822 eqi, eqj = i, j
2025-07-02 05:53:07.829 continue
2025-07-02 05:53:07.835 cruncher.set_seq1(ai)
2025-07-02 05:53:07.841 # computing similarity is expensive, so use the quick
2025-07-02 05:53:07.847 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:07.859 # compares by a factor of 3.
2025-07-02 05:53:07.868 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:07.877 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:07.884 # of the computation is cached by cruncher
2025-07-02 05:53:07.898 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:07.909 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:07.921 cruncher.ratio() > best_ratio:
2025-07-02 05:53:07.934 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:07.947 if best_ratio < cutoff:
2025-07-02 05:53:07.955 # no non-identical "pretty close" pair
2025-07-02 05:53:07.965 if eqi is None:
2025-07-02 05:53:07.977 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:07.986 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:07.996 return
2025-07-02 05:53:08.007 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:08.019 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:08.031 else:
2025-07-02 05:53:08.042 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:08.055 eqi = None
2025-07-02 05:53:08.066
2025-07-02 05:53:08.077 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:08.085 # identical
2025-07-02 05:53:08.092
2025-07-02 05:53:08.099 # pump out diffs from before the synch point
2025-07-02 05:53:08.107 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:08.119
2025-07-02 05:53:08.129 # do intraline marking on the synch pair
2025-07-02 05:53:08.136 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:08.143 if eqi is None:
2025-07-02 05:53:08.151 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:08.162 atags = btags = ""
2025-07-02 05:53:08.173 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:08.183 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:08.190 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:08.198 if tag == 'replace':
2025-07-02 05:53:08.204 atags += '^' * la
2025-07-02 05:53:08.210 btags += '^' * lb
2025-07-02 05:53:08.215 elif tag == 'delete':
2025-07-02 05:53:08.221 atags += '-' * la
2025-07-02 05:53:08.227 elif tag == 'insert':
2025-07-02 05:53:08.233 btags += '+' * lb
2025-07-02 05:53:08.240 elif tag == 'equal':
2025-07-02 05:53:08.246 atags += ' ' * la
2025-07-02 05:53:08.259 btags += ' ' * lb
2025-07-02 05:53:08.273 else:
2025-07-02 05:53:08.281 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:08.289 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:08.295 else:
2025-07-02 05:53:08.303 # the synch pair is identical
2025-07-02 05:53:08.314 yield '  ' + aelt
2025-07-02 05:53:08.325
2025-07-02 05:53:08.336 # pump out diffs from after the synch point
2025-07-02 05:53:08.345 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:08.359
2025-07-02 05:53:08.370 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:08.382 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:08.393
2025-07-02 05:53:08.406 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:08.418 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:08.427 alo = 485, ahi = 1101
2025-07-02 05:53:08.436 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:08.443 blo = 485, bhi = 1101
2025-07-02 05:53:08.449
2025-07-02 05:53:08.456 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:08.462 g = []
2025-07-02 05:53:08.468 if alo < ahi:
2025-07-02 05:53:08.474 if blo < bhi:
2025-07-02 05:53:08.480 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:08.488 else:
2025-07-02 05:53:08.498 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:08.506 elif blo < bhi:
2025-07-02 05:53:08.513 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:08.518
2025-07-02 05:53:08.523 >       yield from g
2025-07-02 05:53:08.536
2025-07-02 05:53:08.543 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:08.549 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:08.555
2025-07-02 05:53:08.560 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:08.565 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:08.573 alo = 485, ahi = 1101
2025-07-02 05:53:08.585 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:08.594 blo = 485, bhi = 1101
2025-07-02 05:53:08.604
2025-07-02 05:53:08.613 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:08.620 r"""
2025-07-02 05:53:08.634 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:08.645 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:08.654 synch point, and intraline difference marking is done on the
2025-07-02 05:53:08.661 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:08.673
2025-07-02 05:53:08.684 Example:
2025-07-02 05:53:08.692
2025-07-02 05:53:08.700 >>> d = Differ()
2025-07-02 05:53:08.707 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:08.714 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:08.722 >>> print(''.join(results), end="")
2025-07-02 05:53:08.730 - abcDefghiJkl
2025-07-02 05:53:08.744 + abcdefGhijkl
2025-07-02 05:53:08.761 """
2025-07-02 05:53:08.769
2025-07-02 05:53:08.776 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:08.783 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:08.791 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:08.804 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:08.815 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:08.824
2025-07-02 05:53:08.838 # search for the pair that matches best without being identical
2025-07-02 05:53:08.850 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:08.859 # on junk -- unless we have to)
2025-07-02 05:53:08.867 for j in range(blo, bhi):
2025-07-02 05:53:08.878 bj = b[j]
2025-07-02 05:53:08.887 cruncher.set_seq2(bj)
2025-07-02 05:53:08.900 for i in range(alo, ahi):
2025-07-02 05:53:08.917 ai = a[i]
2025-07-02 05:53:08.924 if ai == bj:
2025-07-02 05:53:08.931 if eqi is None:
2025-07-02 05:53:08.936 eqi, eqj = i, j
2025-07-02 05:53:08.942 continue
2025-07-02 05:53:08.947 cruncher.set_seq1(ai)
2025-07-02 05:53:08.952 # computing similarity is expensive, so use the quick
2025-07-02 05:53:08.958 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:08.963 # compares by a factor of 3.
2025-07-02 05:53:08.968 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:08.973 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:08.979 # of the computation is cached by cruncher
2025-07-02 05:53:08.988 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:08.999 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:09.007 cruncher.ratio() > best_ratio:
2025-07-02 05:53:09.020 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:09.034 if best_ratio < cutoff:
2025-07-02 05:53:09.044 # no non-identical "pretty close" pair
2025-07-02 05:53:09.051 if eqi is None:
2025-07-02 05:53:09.059 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:09.069 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:09.080 return
2025-07-02 05:53:09.091 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:09.103 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:09.112 else:
2025-07-02 05:53:09.120 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:09.127 eqi = None
2025-07-02 05:53:09.133
2025-07-02 05:53:09.138 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:09.150 # identical
2025-07-02 05:53:09.160
2025-07-02 05:53:09.169 # pump out diffs from before the synch point
2025-07-02 05:53:09.176 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:09.183
2025-07-02 05:53:09.191 # do intraline marking on the synch pair
2025-07-02 05:53:09.199 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:09.207 if eqi is None:
2025-07-02 05:53:09.213 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:09.219 atags = btags = ""
2025-07-02 05:53:09.225 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:09.231 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:09.237 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:09.242 if tag == 'replace':
2025-07-02 05:53:09.248 atags += '^' * la
2025-07-02 05:53:09.253 btags += '^' * lb
2025-07-02 05:53:09.259 elif tag == 'delete':
2025-07-02 05:53:09.265 atags += '-' * la
2025-07-02 05:53:09.271 elif tag == 'insert':
2025-07-02 05:53:09.279 btags += '+' * lb
2025-07-02 05:53:09.289 elif tag == 'equal':
2025-07-02 05:53:09.297 atags += ' ' * la
2025-07-02 05:53:09.304 btags += ' ' * lb
2025-07-02 05:53:09.310 else:
2025-07-02 05:53:09.320 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:09.330 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:09.337 else:
2025-07-02 05:53:09.347 # the synch pair is identical
2025-07-02 05:53:09.357 yield '  ' + aelt
2025-07-02 05:53:09.364
2025-07-02 05:53:09.372 # pump out diffs from after the synch point
2025-07-02 05:53:09.379 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:09.384
2025-07-02 05:53:09.390 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:09.399 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:09.409
2025-07-02 05:53:09.423 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:09.433 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:09.440 alo = 488, ahi = 1101
2025-07-02 05:53:09.453 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:09.463 blo = 488, bhi = 1101
2025-07-02 05:53:09.474
2025-07-02 05:53:09.487 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:09.497 g = []
2025-07-02 05:53:09.505 if alo < ahi:
2025-07-02 05:53:09.512 if blo < bhi:
2025-07-02 05:53:09.519 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:09.526 else:
2025-07-02 05:53:09.536 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:09.548 elif blo < bhi:
2025-07-02 05:53:09.558 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:09.566
2025-07-02 05:53:09.577 >       yield from g
2025-07-02 05:53:09.589
2025-07-02 05:53:09.599 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:09.611 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:09.621
2025-07-02 05:53:09.631 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:09.646 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:09.655 alo = 488, ahi = 1101
2025-07-02 05:53:09.665 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:09.672 blo = 488, bhi = 1101
2025-07-02 05:53:09.681
2025-07-02 05:53:09.688 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:09.695 r"""
2025-07-02 05:53:09.703 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:09.713 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:09.722 synch point, and intraline difference marking is done on the
2025-07-02 05:53:09.729 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:09.736
2025-07-02 05:53:09.742 Example:
2025-07-02 05:53:09.748
2025-07-02 05:53:09.758 >>> d = Differ()
2025-07-02 05:53:09.769 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:09.782 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:09.793 >>> print(''.join(results), end="")
2025-07-02 05:53:09.805 - abcDefghiJkl
2025-07-02 05:53:09.830 + abcdefGhijkl
2025-07-02 05:53:09.853 """
2025-07-02 05:53:09.861
2025-07-02 05:53:09.872 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:09.882 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:09.891 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:09.904 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:09.916 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:09.927
2025-07-02 05:53:09.935 # search for the pair that matches best without being identical
2025-07-02 05:53:09.943 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:09.950 # on junk -- unless we have to)
2025-07-02 05:53:09.957 for j in range(blo, bhi):
2025-07-02 05:53:09.966 bj = b[j]
2025-07-02 05:53:09.978 cruncher.set_seq2(bj)
2025-07-02 05:53:09.988 for i in range(alo, ahi):
2025-07-02 05:53:09.997 ai = a[i]
2025-07-02 05:53:10.004 if ai == bj:
2025-07-02 05:53:10.011 if eqi is None:
2025-07-02 05:53:10.019 eqi, eqj = i, j
2025-07-02 05:53:10.032 continue
2025-07-02 05:53:10.046 cruncher.set_seq1(ai)
2025-07-02 05:53:10.056 # computing similarity is expensive, so use the quick
2025-07-02 05:53:10.068 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:10.077 # compares by a factor of 3.
2025-07-02 05:53:10.085 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:10.092 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:10.104 # of the computation is cached by cruncher
2025-07-02 05:53:10.114 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:10.122 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:10.132 cruncher.ratio() > best_ratio:
2025-07-02 05:53:10.143 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:10.155 if best_ratio < cutoff:
2025-07-02 05:53:10.164 # no non-identical "pretty close" pair
2025-07-02 05:53:10.172 if eqi is None:
2025-07-02 05:53:10.179 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:10.186 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:10.196 return
2025-07-02 05:53:10.206 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:10.213 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:10.220 else:
2025-07-02 05:53:10.227 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:10.232 eqi = None
2025-07-02 05:53:10.238
2025-07-02 05:53:10.246 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:10.258 # identical
2025-07-02 05:53:10.268
2025-07-02 05:53:10.275 # pump out diffs from before the synch point
2025-07-02 05:53:10.282 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:10.287
2025-07-02 05:53:10.292 # do intraline marking on the synch pair
2025-07-02 05:53:10.298 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:10.303 if eqi is None:
2025-07-02 05:53:10.308 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:10.314 atags = btags = ""
2025-07-02 05:53:10.321 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:10.332 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:10.344 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:10.352 if tag == 'replace':
2025-07-02 05:53:10.359 atags += '^' * la
2025-07-02 05:53:10.365 btags += '^' * lb
2025-07-02 05:53:10.372 elif tag == 'delete':
2025-07-02 05:53:10.377 atags += '-' * la
2025-07-02 05:53:10.382 elif tag == 'insert':
2025-07-02 05:53:10.389 btags += '+' * lb
2025-07-02 05:53:10.397 elif tag == 'equal':
2025-07-02 05:53:10.403 atags += ' ' * la
2025-07-02 05:53:10.411 btags += ' ' * lb
2025-07-02 05:53:10.419 else:
2025-07-02 05:53:10.430 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:10.438 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:10.445 else:
2025-07-02 05:53:10.452 # the synch pair is identical
2025-07-02 05:53:10.460 yield '  ' + aelt
2025-07-02 05:53:10.468
2025-07-02 05:53:10.477 # pump out diffs from after the synch point
2025-07-02 05:53:10.491 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:10.499
2025-07-02 05:53:10.507 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:10.517 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:10.527
2025-07-02 05:53:10.534 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:10.542 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:10.553 alo = 489, ahi = 1101
2025-07-02 05:53:10.561 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:10.569 blo = 489, bhi = 1101
2025-07-02 05:53:10.576
2025-07-02 05:53:10.584 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:10.590 g = []
2025-07-02 05:53:10.601 if alo < ahi:
2025-07-02 05:53:10.613 if blo < bhi:
2025-07-02 05:53:10.621 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:10.628 else:
2025-07-02 05:53:10.634 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:10.639 elif blo < bhi:
2025-07-02 05:53:10.644 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:10.649
2025-07-02 05:53:10.655 >       yield from g
2025-07-02 05:53:10.660
2025-07-02 05:53:10.667 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:10.679 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:10.688
2025-07-02 05:53:10.696 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:10.707 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:10.718 alo = 489, ahi = 1101
2025-07-02 05:53:10.728 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:10.736 blo = 489, bhi = 1101
2025-07-02 05:53:10.744
2025-07-02 05:53:10.750 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:10.757 r"""
2025-07-02 05:53:10.762 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:10.767 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:10.772 synch point, and intraline difference marking is done on the
2025-07-02 05:53:10.777 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:10.782
2025-07-02 05:53:10.788 Example:
2025-07-02 05:53:10.795
2025-07-02 05:53:10.806 >>> d = Differ()
2025-07-02 05:53:10.817 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:10.826 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:10.835 >>> print(''.join(results), end="")
2025-07-02 05:53:10.841 - abcDefghiJkl
2025-07-02 05:53:10.856 + abcdefGhijkl
2025-07-02 05:53:10.877 """
2025-07-02 05:53:10.884
2025-07-02 05:53:10.891 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:10.898 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:10.904 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:10.910 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:10.919 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:10.930
2025-07-02 05:53:10.938 # search for the pair that matches best without being identical
2025-07-02 05:53:10.946 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:10.955 # on junk -- unless we have to)
2025-07-02 05:53:10.964 for j in range(blo, bhi):
2025-07-02 05:53:10.972 bj = b[j]
2025-07-02 05:53:10.982 cruncher.set_seq2(bj)
2025-07-02 05:53:10.992 for i in range(alo, ahi):
2025-07-02 05:53:11.000 ai = a[i]
2025-07-02 05:53:11.013 if ai == bj:
2025-07-02 05:53:11.023 if eqi is None:
2025-07-02 05:53:11.031 eqi, eqj = i, j
2025-07-02 05:53:11.039 continue
2025-07-02 05:53:11.049 cruncher.set_seq1(ai)
2025-07-02 05:53:11.057 # computing similarity is expensive, so use the quick
2025-07-02 05:53:11.065 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:11.073 # compares by a factor of 3.
2025-07-02 05:53:11.080 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:11.088 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:11.096 # of the computation is cached by cruncher
2025-07-02 05:53:11.103 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:11.109 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:11.116 cruncher.ratio() > best_ratio:
2025-07-02 05:53:11.122 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:11.128 if best_ratio < cutoff:
2025-07-02 05:53:11.134 # no non-identical "pretty close" pair
2025-07-02 05:53:11.140 if eqi is None:
2025-07-02 05:53:11.146 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:11.151 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:11.159 return
2025-07-02 05:53:11.167 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:11.179 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:11.192 else:
2025-07-02 05:53:11.202 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:11.211 eqi = None
2025-07-02 05:53:11.219
2025-07-02 05:53:11.231 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:11.239 # identical
2025-07-02 05:53:11.248
2025-07-02 05:53:11.258 # pump out diffs from before the synch point
2025-07-02 05:53:11.270 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:11.284
2025-07-02 05:53:11.298 # do intraline marking on the synch pair
2025-07-02 05:53:11.308 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:11.316 if eqi is None:
2025-07-02 05:53:11.322 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:11.328 atags = btags = ""
2025-07-02 05:53:11.335 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:11.345 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:11.352 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:11.358 if tag == 'replace':
2025-07-02 05:53:11.369 atags += '^' * la
2025-07-02 05:53:11.379 btags += '^' * lb
2025-07-02 05:53:11.386 elif tag == 'delete':
2025-07-02 05:53:11.395 atags += '-' * la
2025-07-02 05:53:11.409 elif tag == 'insert':
2025-07-02 05:53:11.419 btags += '+' * lb
2025-07-02 05:53:11.427 elif tag == 'equal':
2025-07-02 05:53:11.435 atags += ' ' * la
2025-07-02 05:53:11.443 btags += ' ' * lb
2025-07-02 05:53:11.454 else:
2025-07-02 05:53:11.463 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:11.471 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:11.477 else:
2025-07-02 05:53:11.484 # the synch pair is identical
2025-07-02 05:53:11.490 yield '  ' + aelt
2025-07-02 05:53:11.496
2025-07-02 05:53:11.503 # pump out diffs from after the synch point
2025-07-02 05:53:11.510 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:11.521
2025-07-02 05:53:11.531 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:11.542 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:11.550
2025-07-02 05:53:11.559 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:11.569 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:11.582 alo = 490, ahi = 1101
2025-07-02 05:53:11.596 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:11.605 blo = 490, bhi = 1101
2025-07-02 05:53:11.617
2025-07-02 05:53:11.627 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:11.635 g = []
2025-07-02 05:53:11.643 if alo < ahi:
2025-07-02 05:53:11.649 if blo < bhi:
2025-07-02 05:53:11.661 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:11.671 else:
2025-07-02 05:53:11.682 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:11.692 elif blo < bhi:
2025-07-02 05:53:11.704 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:11.716
2025-07-02 05:53:11.728 >       yield from g
2025-07-02 05:53:11.740
2025-07-02 05:53:11.750 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:11.760 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:11.770
2025-07-02 05:53:11.777 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:11.785 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:11.791 alo = 490, ahi = 1101
2025-07-02 05:53:11.798 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:11.803 blo = 490, bhi = 1101
2025-07-02 05:53:11.808
2025-07-02 05:53:11.814 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:11.820 r"""
2025-07-02 05:53:11.828 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:11.835 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:11.841 synch point, and intraline difference marking is done on the
2025-07-02 05:53:11.852 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:11.864
2025-07-02 05:53:11.873 Example:
2025-07-02 05:53:11.885
2025-07-02 05:53:11.896 >>> d = Differ()
2025-07-02 05:53:11.905 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:11.912 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:11.919 >>> print(''.join(results), end="")
2025-07-02 05:53:11.924 - abcDefghiJkl
2025-07-02 05:53:11.936 + abcdefGhijkl
2025-07-02 05:53:11.949 """
2025-07-02 05:53:11.958
2025-07-02 05:53:11.964 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:11.977 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:11.987 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:11.995 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:12.006 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:12.020
2025-07-02 05:53:12.029 # search for the pair that matches best without being identical
2025-07-02 05:53:12.042 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:12.053 # on junk -- unless we have to)
2025-07-02 05:53:12.064 for j in range(blo, bhi):
2025-07-02 05:53:12.073 bj = b[j]
2025-07-02 05:53:12.081 cruncher.set_seq2(bj)
2025-07-02 05:53:12.091 for i in range(alo, ahi):
2025-07-02 05:53:12.097 ai = a[i]
2025-07-02 05:53:12.103 if ai == bj:
2025-07-02 05:53:12.109 if eqi is None:
2025-07-02 05:53:12.115 eqi, eqj = i, j
2025-07-02 05:53:12.122 continue
2025-07-02 05:53:12.127 cruncher.set_seq1(ai)
2025-07-02 05:53:12.133 # computing similarity is expensive, so use the quick
2025-07-02 05:53:12.137 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:12.142 # compares by a factor of 3.
2025-07-02 05:53:12.147 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:12.153 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:12.158 # of the computation is cached by cruncher
2025-07-02 05:53:12.165 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:12.177 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:12.188 cruncher.ratio() > best_ratio:
2025-07-02 05:53:12.201 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:12.213 if best_ratio < cutoff:
2025-07-02 05:53:12.224 # no non-identical "pretty close" pair
2025-07-02 05:53:12.236 if eqi is None:
2025-07-02 05:53:12.249 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:12.263 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:12.272 return
2025-07-02 05:53:12.280 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:12.287 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:12.293 else:
2025-07-02 05:53:12.300 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:12.306 eqi = None
2025-07-02 05:53:12.312
2025-07-02 05:53:12.319 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:12.327 # identical
2025-07-02 05:53:12.335
2025-07-02 05:53:12.343 # pump out diffs from before the synch point
2025-07-02 05:53:12.349 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:12.357
2025-07-02 05:53:12.365 # do intraline marking on the synch pair
2025-07-02 05:53:12.372 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:12.377 if eqi is None:
2025-07-02 05:53:12.383 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:12.393 atags = btags = ""
2025-07-02 05:53:12.405 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:12.414 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:12.422 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:12.428 if tag == 'replace':
2025-07-02 05:53:12.435 atags += '^' * la
2025-07-02 05:53:12.441 btags += '^' * lb
2025-07-02 05:53:12.446 elif tag == 'delete':
2025-07-02 05:53:12.452 atags += '-' * la
2025-07-02 05:53:12.458 elif tag == 'insert':
2025-07-02 05:53:12.467 btags += '+' * lb
2025-07-02 05:53:12.479 elif tag == 'equal':
2025-07-02 05:53:12.492 atags += ' ' * la
2025-07-02 05:53:12.505 btags += ' ' * lb
2025-07-02 05:53:12.516 else:
2025-07-02 05:53:12.526 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:12.536 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:12.542 else:
2025-07-02 05:53:12.553 # the synch pair is identical
2025-07-02 05:53:12.563 yield '  ' + aelt
2025-07-02 05:53:12.571
2025-07-02 05:53:12.582 # pump out diffs from after the synch point
2025-07-02 05:53:12.591 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:12.599
2025-07-02 05:53:12.609 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:12.622 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:12.632
2025-07-02 05:53:12.644 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:12.658 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:12.670 alo = 491, ahi = 1101
2025-07-02 05:53:12.682 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:12.695 blo = 491, bhi = 1101
2025-07-02 05:53:12.706
2025-07-02 05:53:12.714 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:12.721 g = []
2025-07-02 05:53:12.731 if alo < ahi:
2025-07-02 05:53:12.744 if blo < bhi:
2025-07-02 05:53:12.754 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:12.763 else:
2025-07-02 05:53:12.770 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:12.777 elif blo < bhi:
2025-07-02 05:53:12.783 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:12.797
2025-07-02 05:53:12.807 >       yield from g
2025-07-02 05:53:12.816
2025-07-02 05:53:12.824 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:12.832 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:12.839
2025-07-02 05:53:12.847 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:12.859 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:12.869 alo = 491, ahi = 1101
2025-07-02 05:53:12.878 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:12.887 blo = 491, bhi = 1101
2025-07-02 05:53:12.896
2025-07-02 05:53:12.904 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:12.911 r"""
2025-07-02 05:53:12.918 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:12.925 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:12.931 synch point, and intraline difference marking is done on the
2025-07-02 05:53:12.938 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:12.949
2025-07-02 05:53:12.957 Example:
2025-07-02 05:53:12.970
2025-07-02 05:53:12.979 >>> d = Differ()
2025-07-02 05:53:12.989 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:12.997 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:13.004 >>> print(''.join(results), end="")
2025-07-02 05:53:13.011 - abcDefghiJkl
2025-07-02 05:53:13.031 + abcdefGhijkl
2025-07-02 05:53:13.050 """
2025-07-02 05:53:13.056
2025-07-02 05:53:13.063 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:13.075 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:13.085 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:13.093 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:13.106 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:13.121
2025-07-02 05:53:13.129 # search for the pair that matches best without being identical
2025-07-02 05:53:13.140 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:13.149 # on junk -- unless we have to)
2025-07-02 05:53:13.161 for j in range(blo, bhi):
2025-07-02 05:53:13.173 bj = b[j]
2025-07-02 05:53:13.185 cruncher.set_seq2(bj)
2025-07-02 05:53:13.196 for i in range(alo, ahi):
2025-07-02 05:53:13.205 ai = a[i]
2025-07-02 05:53:13.216 if ai == bj:
2025-07-02 05:53:13.227 if eqi is None:
2025-07-02 05:53:13.235 eqi, eqj = i, j
2025-07-02 05:53:13.244 continue
2025-07-02 05:53:13.255 cruncher.set_seq1(ai)
2025-07-02 05:53:13.263 # computing similarity is expensive, so use the quick
2025-07-02 05:53:13.271 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:13.282 # compares by a factor of 3.
2025-07-02 05:53:13.294 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:13.305 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:13.317 # of the computation is cached by cruncher
2025-07-02 05:53:13.328 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:13.337 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:13.349 cruncher.ratio() > best_ratio:
2025-07-02 05:53:13.358 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:13.366 if best_ratio < cutoff:
2025-07-02 05:53:13.375 # no non-identical "pretty close" pair
2025-07-02 05:53:13.387 if eqi is None:
2025-07-02 05:53:13.397 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:13.405 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:13.411 return
2025-07-02 05:53:13.423 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:13.433 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:13.441 else:
2025-07-02 05:53:13.448 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:13.454 eqi = None
2025-07-02 05:53:13.459
2025-07-02 05:53:13.467 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:13.477 # identical
2025-07-02 05:53:13.486
2025-07-02 05:53:13.493 # pump out diffs from before the synch point
2025-07-02 05:53:13.504 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:13.513
2025-07-02 05:53:13.522 # do intraline marking on the synch pair
2025-07-02 05:53:13.531 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:13.539 if eqi is None:
2025-07-02 05:53:13.549 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:13.558 atags = btags = ""
2025-07-02 05:53:13.566 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:13.576 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:13.586 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:13.595 if tag == 'replace':
2025-07-02 05:53:13.606 atags += '^' * la
2025-07-02 05:53:13.616 btags += '^' * lb
2025-07-02 05:53:13.628 elif tag == 'delete':
2025-07-02 05:53:13.638 atags += '-' * la
2025-07-02 05:53:13.646 elif tag == 'insert':
2025-07-02 05:53:13.656 btags += '+' * lb
2025-07-02 05:53:13.665 elif tag == 'equal':
2025-07-02 05:53:13.673 atags += ' ' * la
2025-07-02 05:53:13.685 btags += ' ' * lb
2025-07-02 05:53:13.693 else:
2025-07-02 05:53:13.702 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:13.711 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:13.724 else:
2025-07-02 05:53:13.734 # the synch pair is identical
2025-07-02 05:53:13.746 yield '  ' + aelt
2025-07-02 05:53:13.758
2025-07-02 05:53:13.766 # pump out diffs from after the synch point
2025-07-02 05:53:13.774 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:13.781
2025-07-02 05:53:13.788 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:13.794 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:13.800
2025-07-02 05:53:13.808 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:13.821 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:13.829 alo = 492, ahi = 1101
2025-07-02 05:53:13.838 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:13.850 blo = 492, bhi = 1101
2025-07-02 05:53:13.858
2025-07-02 05:53:13.868 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:13.877 g = []
2025-07-02 05:53:13.884 if alo < ahi:
2025-07-02 05:53:13.892 if blo < bhi:
2025-07-02 05:53:13.898 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:13.904 else:
2025-07-02 05:53:13.909 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:13.914 elif blo < bhi:
2025-07-02 05:53:13.918 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:13.930
2025-07-02 05:53:13.938 >       yield from g
2025-07-02 05:53:13.946
2025-07-02 05:53:13.953 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:13.959 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:13.968
2025-07-02 05:53:13.978 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:13.987 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:13.997 alo = 492, ahi = 1101
2025-07-02 05:53:14.006 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:14.017 blo = 492, bhi = 1101
2025-07-02 05:53:14.030
2025-07-02 05:53:14.041 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:14.050 r"""
2025-07-02 05:53:14.061 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:14.081 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:14.090 synch point, and intraline difference marking is done on the
2025-07-02 05:53:14.098 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:14.109
2025-07-02 05:53:14.121 Example:
2025-07-02 05:53:14.135
2025-07-02 05:53:14.144 >>> d = Differ()
2025-07-02 05:53:14.153 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:14.164 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:14.175 >>> print(''.join(results), end="")
2025-07-02 05:53:14.185 - abcDefghiJkl
2025-07-02 05:53:14.209 + abcdefGhijkl
2025-07-02 05:53:14.224 """
2025-07-02 05:53:14.230
2025-07-02 05:53:14.236 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:14.241 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:14.247 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:14.256 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:14.266 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:14.273
2025-07-02 05:53:14.279 # search for the pair that matches best without being identical
2025-07-02 05:53:14.284 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:14.289 # on junk -- unless we have to)
2025-07-02 05:53:14.297 for j in range(blo, bhi):
2025-07-02 05:53:14.303 bj = b[j]
2025-07-02 05:53:14.309 cruncher.set_seq2(bj)
2025-07-02 05:53:14.315 for i in range(alo, ahi):
2025-07-02 05:53:14.327 ai = a[i]
2025-07-02 05:53:14.341 if ai == bj:
2025-07-02 05:53:14.352 if eqi is None:
2025-07-02 05:53:14.365 eqi, eqj = i, j
2025-07-02 05:53:14.374 continue
2025-07-02 05:53:14.384 cruncher.set_seq1(ai)
2025-07-02 05:53:14.399 # computing similarity is expensive, so use the quick
2025-07-02 05:53:14.412 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:14.426 # compares by a factor of 3.
2025-07-02 05:53:14.438 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:14.451 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:14.460 # of the computation is cached by cruncher
2025-07-02 05:53:14.474 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:14.485 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:14.497 cruncher.ratio() > best_ratio:
2025-07-02 05:53:14.510 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:14.521 if best_ratio < cutoff:
2025-07-02 05:53:14.534 # no non-identical "pretty close" pair
2025-07-02 05:53:14.545 if eqi is None:
2025-07-02 05:53:14.557 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:14.568 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:14.577 return
2025-07-02 05:53:14.584 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:14.590 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:14.596 else:
2025-07-02 05:53:14.602 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:14.608 eqi = None
2025-07-02 05:53:14.617
2025-07-02 05:53:14.626 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:14.638 # identical
2025-07-02 05:53:14.648
2025-07-02 05:53:14.655 # pump out diffs from before the synch point
2025-07-02 05:53:14.663 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:14.671
2025-07-02 05:53:14.682 # do intraline marking on the synch pair
2025-07-02 05:53:14.690 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:14.698 if eqi is None:
2025-07-02 05:53:14.706 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:14.712 atags = btags = ""
2025-07-02 05:53:14.725 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:14.738 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:14.749 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:14.762 if tag == 'replace':
2025-07-02 05:53:14.773 atags += '^' * la
2025-07-02 05:53:14.783 btags += '^' * lb
2025-07-02 05:53:14.794 elif tag == 'delete':
2025-07-02 05:53:14.805 atags += '-' * la
2025-07-02 05:53:14.815 elif tag == 'insert':
2025-07-02 05:53:14.824 btags += '+' * lb
2025-07-02 05:53:14.831 elif tag == 'equal':
2025-07-02 05:53:14.837 atags += ' ' * la
2025-07-02 05:53:14.843 btags += ' ' * lb
2025-07-02 05:53:14.848 else:
2025-07-02 05:53:14.853 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:14.865 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:14.872 else:
2025-07-02 05:53:14.879 # the synch pair is identical
2025-07-02 05:53:14.885 yield '  ' + aelt
2025-07-02 05:53:14.890
2025-07-02 05:53:14.900 # pump out diffs from after the synch point
2025-07-02 05:53:14.909 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:14.917
2025-07-02 05:53:14.929 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:14.938 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:14.946
2025-07-02 05:53:14.957 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:14.967 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:14.975 alo = 493, ahi = 1101
2025-07-02 05:53:14.982 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:14.989 blo = 493, bhi = 1101
2025-07-02 05:53:14.995
2025-07-02 05:53:15.003 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:15.014 g = []
2025-07-02 05:53:15.023 if alo < ahi:
2025-07-02 05:53:15.032 if blo < bhi:
2025-07-02 05:53:15.038 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:15.044 else:
2025-07-02 05:53:15.054 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:15.066 elif blo < bhi:
2025-07-02 05:53:15.077 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:15.088
2025-07-02 05:53:15.102 >       yield from g
2025-07-02 05:53:15.114
2025-07-02 05:53:15.126 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:15.137 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:15.147
2025-07-02 05:53:15.154 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:15.163 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:15.175 alo = 493, ahi = 1101
2025-07-02 05:53:15.183 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:15.189 blo = 493, bhi = 1101
2025-07-02 05:53:15.196
2025-07-02 05:53:15.202 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:15.210 r"""
2025-07-02 05:53:15.218 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:15.226 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:15.232 synch point, and intraline difference marking is done on the
2025-07-02 05:53:15.238 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:15.245
2025-07-02 05:53:15.252 Example:
2025-07-02 05:53:15.259
2025-07-02 05:53:15.266 >>> d = Differ()
2025-07-02 05:53:15.275 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:15.285 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:15.297 >>> print(''.join(results), end="")
2025-07-02 05:53:15.307 - abcDefghiJkl
2025-07-02 05:53:15.326 + abcdefGhijkl
2025-07-02 05:53:15.346 """
2025-07-02 05:53:15.354
2025-07-02 05:53:15.363 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:15.371 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:15.378 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:15.385 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:15.390 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:15.395
2025-07-02 05:53:15.401 # search for the pair that matches best without being identical
2025-07-02 05:53:15.409 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:15.415 # on junk -- unless we have to)
2025-07-02 05:53:15.422 for j in range(blo, bhi):
2025-07-02 05:53:15.428 bj = b[j]
2025-07-02 05:53:15.435 cruncher.set_seq2(bj)
2025-07-02 05:53:15.440 for i in range(alo, ahi):
2025-07-02 05:53:15.446 ai = a[i]
2025-07-02 05:53:15.456 if ai == bj:
2025-07-02 05:53:15.465 if eqi is None:
2025-07-02 05:53:15.472 eqi, eqj = i, j
2025-07-02 05:53:15.479 continue
2025-07-02 05:53:15.485 cruncher.set_seq1(ai)
2025-07-02 05:53:15.492 # computing similarity is expensive, so use the quick
2025-07-02 05:53:15.498 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:15.508 # compares by a factor of 3.
2025-07-02 05:53:15.517 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:15.524 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:15.531 # of the computation is cached by cruncher
2025-07-02 05:53:15.539 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:15.547 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:15.559 cruncher.ratio() > best_ratio:
2025-07-02 05:53:15.566 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:15.573 if best_ratio < cutoff:
2025-07-02 05:53:15.579 # no non-identical "pretty close" pair
2025-07-02 05:53:15.587 if eqi is None:
2025-07-02 05:53:15.599 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:15.606 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:15.614 return
2025-07-02 05:53:15.625 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:15.635 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:15.643 else:
2025-07-02 05:53:15.650 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:15.661 eqi = None
2025-07-02 05:53:15.669
2025-07-02 05:53:15.679 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:15.686 # identical
2025-07-02 05:53:15.695
2025-07-02 05:53:15.704 # pump out diffs from before the synch point
2025-07-02 05:53:15.713 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:15.721
2025-07-02 05:53:15.728 # do intraline marking on the synch pair
2025-07-02 05:53:15.741 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:15.751 if eqi is None:
2025-07-02 05:53:15.763 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:15.772 atags = btags = ""
2025-07-02 05:53:15.780 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:15.792 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:15.803 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:15.811 if tag == 'replace':
2025-07-02 05:53:15.819 atags += '^' * la
2025-07-02 05:53:15.826 btags += '^' * lb
2025-07-02 05:53:15.838 elif tag == 'delete':
2025-07-02 05:53:15.848 atags += '-' * la
2025-07-02 05:53:15.855 elif tag == 'insert':
2025-07-02 05:53:15.863 btags += '+' * lb
2025-07-02 05:53:15.870 elif tag == 'equal':
2025-07-02 05:53:15.882 atags += ' ' * la
2025-07-02 05:53:15.892 btags += ' ' * lb
2025-07-02 05:53:15.899 else:
2025-07-02 05:53:15.906 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:15.914 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:15.919 else:
2025-07-02 05:53:15.928 # the synch pair is identical
2025-07-02 05:53:15.936 yield '  ' + aelt
2025-07-02 05:53:15.943
2025-07-02 05:53:15.949 # pump out diffs from after the synch point
2025-07-02 05:53:15.955 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:15.963
2025-07-02 05:53:15.975 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:15.987 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:15.999
2025-07-02 05:53:16.011 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:16.022 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:16.030 alo = 494, ahi = 1101
2025-07-02 05:53:16.038 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:16.045 blo = 494, bhi = 1101
2025-07-02 05:53:16.051
2025-07-02 05:53:16.058 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:16.064 g = []
2025-07-02 05:53:16.071 if alo < ahi:
2025-07-02 05:53:16.079 if blo < bhi:
2025-07-02 05:53:16.086 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:16.095 else:
2025-07-02 05:53:16.107 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:16.116 elif blo < bhi:
2025-07-02 05:53:16.124 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:16.131
2025-07-02 05:53:16.137 >       yield from g
2025-07-02 05:53:16.142
2025-07-02 05:53:16.154 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:16.167 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:16.176
2025-07-02 05:53:16.184 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:16.192 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:16.204 alo = 494, ahi = 1101
2025-07-02 05:53:16.213 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:16.220 blo = 494, bhi = 1101
2025-07-02 05:53:16.228
2025-07-02 05:53:16.241 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:16.253 r"""
2025-07-02 05:53:16.262 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:16.271 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:16.278 synch point, and intraline difference marking is done on the
2025-07-02 05:53:16.285 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:16.290
2025-07-02 05:53:16.295 Example:
2025-07-02 05:53:16.300
2025-07-02 05:53:16.305 >>> d = Differ()
2025-07-02 05:53:16.310 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:16.317 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:16.325 >>> print(''.join(results), end="")
2025-07-02 05:53:16.332 - abcDefghiJkl
2025-07-02 05:53:16.345 + abcdefGhijkl
2025-07-02 05:53:16.358 """
2025-07-02 05:53:16.367
2025-07-02 05:53:16.377 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:16.385 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:16.393 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:16.406 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:16.417 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:16.425
2025-07-02 05:53:16.431 # search for the pair that matches best without being identical
2025-07-02 05:53:16.436 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:16.441 # on junk -- unless we have to)
2025-07-02 05:53:16.446 for j in range(blo, bhi):
2025-07-02 05:53:16.452 bj = b[j]
2025-07-02 05:53:16.458 cruncher.set_seq2(bj)
2025-07-02 05:53:16.465 for i in range(alo, ahi):
2025-07-02 05:53:16.474 ai = a[i]
2025-07-02 05:53:16.485 if ai == bj:
2025-07-02 05:53:16.493 if eqi is None:
2025-07-02 05:53:16.499 eqi, eqj = i, j
2025-07-02 05:53:16.505 continue
2025-07-02 05:53:16.511 cruncher.set_seq1(ai)
2025-07-02 05:53:16.518 # computing similarity is expensive, so use the quick
2025-07-02 05:53:16.529 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:16.538 # compares by a factor of 3.
2025-07-02 05:53:16.547 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:16.557 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:16.566 # of the computation is cached by cruncher
2025-07-02 05:53:16.578 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:16.589 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:16.596 cruncher.ratio() > best_ratio:
2025-07-02 05:53:16.602 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:16.607 if best_ratio < cutoff:
2025-07-02 05:53:16.611 # no non-identical "pretty close" pair
2025-07-02 05:53:16.620 if eqi is None:
2025-07-02 05:53:16.633 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:16.643 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:16.652 return
2025-07-02 05:53:16.665 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:16.678 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:16.689 else:
2025-07-02 05:53:16.700 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:16.707 eqi = None
2025-07-02 05:53:16.713
2025-07-02 05:53:16.718 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:16.723 # identical
2025-07-02 05:53:16.728
2025-07-02 05:53:16.735 # pump out diffs from before the synch point
2025-07-02 05:53:16.741 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:16.747
2025-07-02 05:53:16.753 # do intraline marking on the synch pair
2025-07-02 05:53:16.764 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:16.771 if eqi is None:
2025-07-02 05:53:16.786 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:16.794 atags = btags = ""
2025-07-02 05:53:16.800 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:16.806 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:16.817 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:16.828 if tag == 'replace':
2025-07-02 05:53:16.837 atags += '^' * la
2025-07-02 05:53:16.844 btags += '^' * lb
2025-07-02 05:53:16.851 elif tag == 'delete':
2025-07-02 05:53:16.857 atags += '-' * la
2025-07-02 05:53:16.869 elif tag == 'insert':
2025-07-02 05:53:16.879 btags += '+' * lb
2025-07-02 05:53:16.888 elif tag == 'equal':
2025-07-02 05:53:16.895 atags += ' ' * la
2025-07-02 05:53:16.901 btags += ' ' * lb
2025-07-02 05:53:16.911 else:
2025-07-02 05:53:16.920 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:16.929 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:16.936 else:
2025-07-02 05:53:16.942 # the synch pair is identical
2025-07-02 05:53:16.949 yield '  ' + aelt
2025-07-02 05:53:16.956
2025-07-02 05:53:16.962 # pump out diffs from after the synch point
2025-07-02 05:53:16.970 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:16.981
2025-07-02 05:53:16.992 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:17.001 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:17.010
2025-07-02 05:53:17.021 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:17.037 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:17.049 alo = 495, ahi = 1101
2025-07-02 05:53:17.059 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:17.067 blo = 495, bhi = 1101
2025-07-02 05:53:17.073
2025-07-02 05:53:17.080 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:17.087 g = []
2025-07-02 05:53:17.094 if alo < ahi:
2025-07-02 05:53:17.100 if blo < bhi:
2025-07-02 05:53:17.106 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:17.116 else:
2025-07-02 05:53:17.125 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:17.133 elif blo < bhi:
2025-07-02 05:53:17.140 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:17.146
2025-07-02 05:53:17.152 >       yield from g
2025-07-02 05:53:17.160
2025-07-02 05:53:17.169 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:17.177 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:17.183
2025-07-02 05:53:17.189 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:17.195 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:17.200 alo = 495, ahi = 1101
2025-07-02 05:53:17.206 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:17.212 blo = 495, bhi = 1101
2025-07-02 05:53:17.221
2025-07-02 05:53:17.229 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:17.239 r"""
2025-07-02 05:53:17.250 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:17.259 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:17.266 synch point, and intraline difference marking is done on the
2025-07-02 05:53:17.273 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:17.280
2025-07-02 05:53:17.286 Example:
2025-07-02 05:53:17.291
2025-07-02 05:53:17.299 >>> d = Differ()
2025-07-02 05:53:17.310 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:17.322 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:17.334 >>> print(''.join(results), end="")
2025-07-02 05:53:17.345 - abcDefghiJkl
2025-07-02 05:53:17.363 + abcdefGhijkl
2025-07-02 05:53:17.379 """
2025-07-02 05:53:17.392
2025-07-02 05:53:17.405 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:17.415 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:17.430 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:17.440 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:17.447 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:17.453
2025-07-02 05:53:17.459 # search for the pair that matches best without being identical
2025-07-02 05:53:17.464 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:17.470 # on junk -- unless we have to)
2025-07-02 05:53:17.475 for j in range(blo, bhi):
2025-07-02 05:53:17.480 bj = b[j]
2025-07-02 05:53:17.488 cruncher.set_seq2(bj)
2025-07-02 05:53:17.499 for i in range(alo, ahi):
2025-07-02 05:53:17.511 ai = a[i]
2025-07-02 05:53:17.520 if ai == bj:
2025-07-02 05:53:17.528 if eqi is None:
2025-07-02 05:53:17.536 eqi, eqj = i, j
2025-07-02 05:53:17.543 continue
2025-07-02 05:53:17.552 cruncher.set_seq1(ai)
2025-07-02 05:53:17.565 # computing similarity is expensive, so use the quick
2025-07-02 05:53:17.579 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:17.589 # compares by a factor of 3.
2025-07-02 05:53:17.598 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:17.606 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:17.613 # of the computation is cached by cruncher
2025-07-02 05:53:17.620 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:17.627 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:17.634 cruncher.ratio() > best_ratio:
2025-07-02 05:53:17.646 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:17.655 if best_ratio < cutoff:
2025-07-02 05:53:17.663 # no non-identical "pretty close" pair
2025-07-02 05:53:17.670 if eqi is None:
2025-07-02 05:53:17.683 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:17.692 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:17.702 return
2025-07-02 05:53:17.715 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:17.726 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:17.736 else:
2025-07-02 05:53:17.747 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:17.756 eqi = None
2025-07-02 05:53:17.765
2025-07-02 05:53:17.771 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:17.780 # identical
2025-07-02 05:53:17.787
2025-07-02 05:53:17.797 # pump out diffs from before the synch point
2025-07-02 05:53:17.807 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:17.816
2025-07-02 05:53:17.824 # do intraline marking on the synch pair
2025-07-02 05:53:17.831 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:17.837 if eqi is None:
2025-07-02 05:53:17.850 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:17.861 atags = btags = ""
2025-07-02 05:53:17.870 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:17.880 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:17.892 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:17.905 if tag == 'replace':
2025-07-02 05:53:17.919 atags += '^' * la
2025-07-02 05:53:17.928 btags += '^' * lb
2025-07-02 05:53:17.935 elif tag == 'delete':
2025-07-02 05:53:17.942 atags += '-' * la
2025-07-02 05:53:17.953 elif tag == 'insert':
2025-07-02 05:53:17.964 btags += '+' * lb
2025-07-02 05:53:17.971 elif tag == 'equal':
2025-07-02 05:53:17.978 atags += ' ' * la
2025-07-02 05:53:17.986 btags += ' ' * lb
2025-07-02 05:53:17.993 else:
2025-07-02 05:53:17.999 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:18.007 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:18.020 else:
2025-07-02 05:53:18.029 # the synch pair is identical
2025-07-02 05:53:18.037 yield '  ' + aelt
2025-07-02 05:53:18.044
2025-07-02 05:53:18.051 # pump out diffs from after the synch point
2025-07-02 05:53:18.059 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:18.068
2025-07-02 05:53:18.080 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:18.089 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:18.095
2025-07-02 05:53:18.103 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:18.112 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:18.120 alo = 496, ahi = 1101
2025-07-02 05:53:18.128 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:18.136 blo = 496, bhi = 1101
2025-07-02 05:53:18.149
2025-07-02 05:53:18.162 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:18.177 g = []
2025-07-02 05:53:18.185 if alo < ahi:
2025-07-02 05:53:18.193 if blo < bhi:
2025-07-02 05:53:18.205 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:18.218 else:
2025-07-02 05:53:18.227 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:18.235 elif blo < bhi:
2025-07-02 05:53:18.249 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:18.261
2025-07-02 05:53:18.272 >       yield from g
2025-07-02 05:53:18.281
2025-07-02 05:53:18.293 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:18.303 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:18.315
2025-07-02 05:53:18.326 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:18.340 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:18.350 alo = 496, ahi = 1101
2025-07-02 05:53:18.359 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:18.366 blo = 496, bhi = 1101
2025-07-02 05:53:18.372
2025-07-02 05:53:18.378 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:18.384 r"""
2025-07-02 05:53:18.393 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:18.407 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:18.421 synch point, and intraline difference marking is done on the
2025-07-02 05:53:18.429 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:18.436
2025-07-02 05:53:18.443 Example:
2025-07-02 05:53:18.449
2025-07-02 05:53:18.462 >>> d = Differ()
2025-07-02 05:53:18.477 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:18.491 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:18.502 >>> print(''.join(results), end="")
2025-07-02 05:53:18.509 - abcDefghiJkl
2025-07-02 05:53:18.523 + abcdefGhijkl
2025-07-02 05:53:18.535 """
2025-07-02 05:53:18.541
2025-07-02 05:53:18.547 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:18.553 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:18.558 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:18.563 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:18.570 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:18.575
2025-07-02 05:53:18.580 # search for the pair that matches best without being identical
2025-07-02 05:53:18.586 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:18.596 # on junk -- unless we have to)
2025-07-02 05:53:18.609 for j in range(blo, bhi):
2025-07-02 05:53:18.619 bj = b[j]
2025-07-02 05:53:18.627 cruncher.set_seq2(bj)
2025-07-02 05:53:18.634 for i in range(alo, ahi):
2025-07-02 05:53:18.640 ai = a[i]
2025-07-02 05:53:18.646 if ai == bj:
2025-07-02 05:53:18.652 if eqi is None:
2025-07-02 05:53:18.659 eqi, eqj = i, j
2025-07-02 05:53:18.670 continue
2025-07-02 05:53:18.680 cruncher.set_seq1(ai)
2025-07-02 05:53:18.691 # computing similarity is expensive, so use the quick
2025-07-02 05:53:18.701 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:18.711 # compares by a factor of 3.
2025-07-02 05:53:18.719 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:18.727 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:18.733 # of the computation is cached by cruncher
2025-07-02 05:53:18.738 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:18.744 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:18.750 cruncher.ratio() > best_ratio:
2025-07-02 05:53:18.755 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:18.761 if best_ratio < cutoff:
2025-07-02 05:53:18.766 # no non-identical "pretty close" pair
2025-07-02 05:53:18.778 if eqi is None:
2025-07-02 05:53:18.789 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:18.798 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:18.809 return
2025-07-02 05:53:18.821 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:18.832 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:18.839 else:
2025-07-02 05:53:18.845 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:18.852 eqi = None
2025-07-02 05:53:18.859
2025-07-02 05:53:18.868 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:18.879 # identical
2025-07-02 05:53:18.890
2025-07-02 05:53:18.899 # pump out diffs from before the synch point
2025-07-02 05:53:18.908 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:18.916
2025-07-02 05:53:18.923 # do intraline marking on the synch pair
2025-07-02 05:53:18.931 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:18.942 if eqi is None:
2025-07-02 05:53:18.950 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:18.958 atags = btags = ""
2025-07-02 05:53:18.969 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:18.983 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:18.995 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:19.008 if tag == 'replace':
2025-07-02 05:53:19.020 atags += '^' * la
2025-07-02 05:53:19.030 btags += '^' * lb
2025-07-02 05:53:19.039 elif tag == 'delete':
2025-07-02 05:53:19.046 atags += '-' * la
2025-07-02 05:53:19.056 elif tag == 'insert':
2025-07-02 05:53:19.071 btags += '+' * lb
2025-07-02 05:53:19.083 elif tag == 'equal':
2025-07-02 05:53:19.095 atags += ' ' * la
2025-07-02 05:53:19.104 btags += ' ' * lb
2025-07-02 05:53:19.112 else:
2025-07-02 05:53:19.119 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:19.126 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:19.138 else:
2025-07-02 05:53:19.147 # the synch pair is identical
2025-07-02 05:53:19.154 yield '  ' + aelt
2025-07-02 05:53:19.160
2025-07-02 05:53:19.166 # pump out diffs from after the synch point
2025-07-02 05:53:19.172 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:19.178
2025-07-02 05:53:19.188 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:19.196 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:19.203
2025-07-02 05:53:19.212 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:19.221 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:19.230 alo = 497, ahi = 1101
2025-07-02 05:53:19.240 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:19.247 blo = 497, bhi = 1101
2025-07-02 05:53:19.261
2025-07-02 05:53:19.272 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:19.283 g = []
2025-07-02 05:53:19.295 if alo < ahi:
2025-07-02 05:53:19.303 if blo < bhi:
2025-07-02 05:53:19.311 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:19.323 else:
2025-07-02 05:53:19.332 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:19.339 elif blo < bhi:
2025-07-02 05:53:19.345 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:19.352
2025-07-02 05:53:19.358 >       yield from g
2025-07-02 05:53:19.364
2025-07-02 05:53:19.369 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:19.375 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:19.381
2025-07-02 05:53:19.392 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:19.401 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:19.407 alo = 497, ahi = 1101
2025-07-02 05:53:19.415 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:19.423 blo = 497, bhi = 1101
2025-07-02 05:53:19.433
2025-07-02 05:53:19.442 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:19.452 r"""
2025-07-02 05:53:19.464 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:19.473 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:19.481 synch point, and intraline difference marking is done on the
2025-07-02 05:53:19.487 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:19.493
2025-07-02 05:53:19.497 Example:
2025-07-02 05:53:19.502
2025-07-02 05:53:19.508 >>> d = Differ()
2025-07-02 05:53:19.515 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:19.520 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:19.525 >>> print(''.join(results), end="")
2025-07-02 05:53:19.529 - abcDefghiJkl
2025-07-02 05:53:19.540 + abcdefGhijkl
2025-07-02 05:53:19.553 """
2025-07-02 05:53:19.559
2025-07-02 05:53:19.572 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:19.581 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:19.588 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:19.595 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:19.603 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:19.612
2025-07-02 05:53:19.623 # search for the pair that matches best without being identical
2025-07-02 05:53:19.633 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:19.639 # on junk -- unless we have to)
2025-07-02 05:53:19.646 for j in range(blo, bhi):
2025-07-02 05:53:19.650 bj = b[j]
2025-07-02 05:53:19.655 cruncher.set_seq2(bj)
2025-07-02 05:53:19.660 for i in range(alo, ahi):
2025-07-02 05:53:19.664 ai = a[i]
2025-07-02 05:53:19.669 if ai == bj:
2025-07-02 05:53:19.676 if eqi is None:
2025-07-02 05:53:19.682 eqi, eqj = i, j
2025-07-02 05:53:19.689 continue
2025-07-02 05:53:19.696 cruncher.set_seq1(ai)
2025-07-02 05:53:19.702 # computing similarity is expensive, so use the quick
2025-07-02 05:53:19.708 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:19.714 # compares by a factor of 3.
2025-07-02 05:53:19.719 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:19.724 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:19.730 # of the computation is cached by cruncher
2025-07-02 05:53:19.736 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:19.742 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:19.750 cruncher.ratio() > best_ratio:
2025-07-02 05:53:19.756 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:19.760 if best_ratio < cutoff:
2025-07-02 05:53:19.766 # no non-identical "pretty close" pair
2025-07-02 05:53:19.778 if eqi is None:
2025-07-02 05:53:19.790 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:19.802 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:19.814 return
2025-07-02 05:53:19.829 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:19.841 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:19.853 else:
2025-07-02 05:53:19.866 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:19.876 eqi = None
2025-07-02 05:53:19.883
2025-07-02 05:53:19.890 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:19.903 # identical
2025-07-02 05:53:19.913
2025-07-02 05:53:19.921 # pump out diffs from before the synch point
2025-07-02 05:53:19.929 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:19.936
2025-07-02 05:53:19.943 # do intraline marking on the synch pair
2025-07-02 05:53:19.951 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:19.962 if eqi is None:
2025-07-02 05:53:19.971 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:19.982 atags = btags = ""
2025-07-02 05:53:19.993 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:20.004 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:20.013 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:20.020 if tag == 'replace':
2025-07-02 05:53:20.028 atags += '^' * la
2025-07-02 05:53:20.035 btags += '^' * lb
2025-07-02 05:53:20.042 elif tag == 'delete':
2025-07-02 05:53:20.056 atags += '-' * la
2025-07-02 05:53:20.068 elif tag == 'insert':
2025-07-02 05:53:20.080 btags += '+' * lb
2025-07-02 05:53:20.089 elif tag == 'equal':
2025-07-02 05:53:20.099 atags += ' ' * la
2025-07-02 05:53:20.111 btags += ' ' * lb
2025-07-02 05:53:20.121 else:
2025-07-02 05:53:20.133 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:20.144 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:20.155 else:
2025-07-02 05:53:20.167 # the synch pair is identical
2025-07-02 05:53:20.181 yield '  ' + aelt
2025-07-02 05:53:20.191
2025-07-02 05:53:20.200 # pump out diffs from after the synch point
2025-07-02 05:53:20.208 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:20.214
2025-07-02 05:53:20.221 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:20.232 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:20.244
2025-07-02 05:53:20.257 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:20.269 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:20.279 alo = 498, ahi = 1101
2025-07-02 05:53:20.296 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:20.304 blo = 498, bhi = 1101
2025-07-02 05:53:20.317
2025-07-02 05:53:20.329 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:20.338 g = []
2025-07-02 05:53:20.349 if alo < ahi:
2025-07-02 05:53:20.356 if blo < bhi:
2025-07-02 05:53:20.363 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:20.368 else:
2025-07-02 05:53:20.374 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:20.379 elif blo < bhi:
2025-07-02 05:53:20.384 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:20.390
2025-07-02 05:53:20.395 >       yield from g
2025-07-02 05:53:20.400
2025-07-02 05:53:20.405 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:20.410 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:20.414
2025-07-02 05:53:20.422 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:20.431 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:20.437 alo = 498, ahi = 1101
2025-07-02 05:53:20.446 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:20.453 blo = 498, bhi = 1101
2025-07-02 05:53:20.460
2025-07-02 05:53:20.467 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:20.473 r"""
2025-07-02 05:53:20.479 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:20.486 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:20.493 synch point, and intraline difference marking is done on the
2025-07-02 05:53:20.499 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:20.507
2025-07-02 05:53:20.516 Example:
2025-07-02 05:53:20.524
2025-07-02 05:53:20.530 >>> d = Differ()
2025-07-02 05:53:20.536 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:20.542 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:20.553 >>> print(''.join(results), end="")
2025-07-02 05:53:20.561 - abcDefghiJkl
2025-07-02 05:53:20.585 + abcdefGhijkl
2025-07-02 05:53:20.608 """
2025-07-02 05:53:20.619
2025-07-02 05:53:20.631 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:20.644 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:20.652 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:20.660 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:20.668 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:20.674
2025-07-02 05:53:20.686 # search for the pair that matches best without being identical
2025-07-02 05:53:20.696 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:20.709 # on junk -- unless we have to)
2025-07-02 05:53:20.719 for j in range(blo, bhi):
2025-07-02 05:53:20.730 bj = b[j]
2025-07-02 05:53:20.738 cruncher.set_seq2(bj)
2025-07-02 05:53:20.746 for i in range(alo, ahi):
2025-07-02 05:53:20.757 ai = a[i]
2025-07-02 05:53:20.768 if ai == bj:
2025-07-02 05:53:20.777 if eqi is None:
2025-07-02 05:53:20.788 eqi, eqj = i, j
2025-07-02 05:53:20.798 continue
2025-07-02 05:53:20.809 cruncher.set_seq1(ai)
2025-07-02 05:53:20.823 # computing similarity is expensive, so use the quick
2025-07-02 05:53:20.837 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:20.850 # compares by a factor of 3.
2025-07-02 05:53:20.861 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:20.872 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:20.881 # of the computation is cached by cruncher
2025-07-02 05:53:20.889 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:20.896 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:20.908 cruncher.ratio() > best_ratio:
2025-07-02 05:53:20.919 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:20.931 if best_ratio < cutoff:
2025-07-02 05:53:20.941 # no non-identical "pretty close" pair
2025-07-02 05:53:20.949 if eqi is None:
2025-07-02 05:53:20.956 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:20.969 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:20.977 return
2025-07-02 05:53:20.984 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:20.992 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:20.998 else:
2025-07-02 05:53:21.003 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:21.008 eqi = None
2025-07-02 05:53:21.012
2025-07-02 05:53:21.018 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:21.028 # identical
2025-07-02 05:53:21.037
2025-07-02 05:53:21.046 # pump out diffs from before the synch point
2025-07-02 05:53:21.053 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:21.060
2025-07-02 05:53:21.067 # do intraline marking on the synch pair
2025-07-02 05:53:21.073 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:21.078 if eqi is None:
2025-07-02 05:53:21.083 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:21.091 atags = btags = ""
2025-07-02 05:53:21.100 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:21.108 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:21.115 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:21.122 if tag == 'replace':
2025-07-02 05:53:21.129 atags += '^' * la
2025-07-02 05:53:21.134 btags += '^' * lb
2025-07-02 05:53:21.147 elif tag == 'delete':
2025-07-02 05:53:21.156 atags += '-' * la
2025-07-02 05:53:21.163 elif tag == 'insert':
2025-07-02 05:53:21.169 btags += '+' * lb
2025-07-02 05:53:21.174 elif tag == 'equal':
2025-07-02 05:53:21.178 atags += ' ' * la
2025-07-02 05:53:21.183 btags += ' ' * lb
2025-07-02 05:53:21.188 else:
2025-07-02 05:53:21.193 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:21.198 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:21.202 else:
2025-07-02 05:53:21.207 # the synch pair is identical
2025-07-02 05:53:21.211 yield '  ' + aelt
2025-07-02 05:53:21.215
2025-07-02 05:53:21.220 # pump out diffs from after the synch point
2025-07-02 05:53:21.225 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:21.229
2025-07-02 05:53:21.233 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:21.238 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:21.242
2025-07-02 05:53:21.247 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:21.252 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:21.257 alo = 499, ahi = 1101
2025-07-02 05:53:21.263 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:21.269 blo = 499, bhi = 1101
2025-07-02 05:53:21.275
2025-07-02 05:53:21.281 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:21.287 g = []
2025-07-02 05:53:21.295 if alo < ahi:
2025-07-02 05:53:21.300 if blo < bhi:
2025-07-02 05:53:21.305 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:21.311 else:
2025-07-02 05:53:21.316 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:21.321 elif blo < bhi:
2025-07-02 05:53:21.326 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:21.330
2025-07-02 05:53:21.335 >       yield from g
2025-07-02 05:53:21.339
2025-07-02 05:53:21.344 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:21.352 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:21.358
2025-07-02 05:53:21.363 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:21.371 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:21.378 alo = 499, ahi = 1101
2025-07-02 05:53:21.386 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:21.392 blo = 499, bhi = 1101
2025-07-02 05:53:21.398
2025-07-02 05:53:21.404 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:21.410 r"""
2025-07-02 05:53:21.415 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:21.423 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:21.436 synch point, and intraline difference marking is done on the
2025-07-02 05:53:21.445 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:21.452
2025-07-02 05:53:21.459 Example:
2025-07-02 05:53:21.468
2025-07-02 05:53:21.480 >>> d = Differ()
2025-07-02 05:53:21.492 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:21.501 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:21.508 >>> print(''.join(results), end="")
2025-07-02 05:53:21.514 - abcDefghiJkl
2025-07-02 05:53:21.528 + abcdefGhijkl
2025-07-02 05:53:21.543 """
2025-07-02 05:53:21.555
2025-07-02 05:53:21.565 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:21.576 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:21.587 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:21.597 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:21.607 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:21.615
2025-07-02 05:53:21.632 # search for the pair that matches best without being identical
2025-07-02 05:53:21.640 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:21.649 # on junk -- unless we have to)
2025-07-02 05:53:21.662 for j in range(blo, bhi):
2025-07-02 05:53:21.672 bj = b[j]
2025-07-02 05:53:21.684 cruncher.set_seq2(bj)
2025-07-02 05:53:21.696 for i in range(alo, ahi):
2025-07-02 05:53:21.707 ai = a[i]
2025-07-02 05:53:21.715 if ai == bj:
2025-07-02 05:53:21.722 if eqi is None:
2025-07-02 05:53:21.731 eqi, eqj = i, j
2025-07-02 05:53:21.742 continue
2025-07-02 05:53:21.754 cruncher.set_seq1(ai)
2025-07-02 05:53:21.765 # computing similarity is expensive, so use the quick
2025-07-02 05:53:21.775 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:21.789 # compares by a factor of 3.
2025-07-02 05:53:21.799 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:21.810 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:21.819 # of the computation is cached by cruncher
2025-07-02 05:53:21.827 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:21.834 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:21.841 cruncher.ratio() > best_ratio:
2025-07-02 05:53:21.850 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:21.864 if best_ratio < cutoff:
2025-07-02 05:53:21.875 # no non-identical "pretty close" pair
2025-07-02 05:53:21.883 if eqi is None:
2025-07-02 05:53:21.893 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:21.906 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:21.917 return
2025-07-02 05:53:21.926 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:21.937 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:21.947 else:
2025-07-02 05:53:21.955 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:21.963 eqi = None
2025-07-02 05:53:21.969
2025-07-02 05:53:21.982 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:21.993 # identical
2025-07-02 05:53:22.005
2025-07-02 05:53:22.016 # pump out diffs from before the synch point
2025-07-02 05:53:22.030 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:22.040
2025-07-02 05:53:22.049 # do intraline marking on the synch pair
2025-07-02 05:53:22.062 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:22.070 if eqi is None:
2025-07-02 05:53:22.080 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:22.091 atags = btags = ""
2025-07-02 05:53:22.104 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:22.116 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:22.126 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:22.136 if tag == 'replace':
2025-07-02 05:53:22.145 atags += '^' * la
2025-07-02 05:53:22.153 btags += '^' * lb
2025-07-02 05:53:22.159 elif tag == 'delete':
2025-07-02 05:53:22.170 atags += '-' * la
2025-07-02 05:53:22.182 elif tag == 'insert':
2025-07-02 05:53:22.194 btags += '+' * lb
2025-07-02 05:53:22.203 elif tag == 'equal':
2025-07-02 05:53:22.210 atags += ' ' * la
2025-07-02 05:53:22.217 btags += ' ' * lb
2025-07-02 05:53:22.223 else:
2025-07-02 05:53:22.229 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:22.235 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:22.242 else:
2025-07-02 05:53:22.255 # the synch pair is identical
2025-07-02 05:53:22.263 yield '  ' + aelt
2025-07-02 05:53:22.269
2025-07-02 05:53:22.275 # pump out diffs from after the synch point
2025-07-02 05:53:22.283 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:22.295
2025-07-02 05:53:22.303 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:22.310 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:22.316
2025-07-02 05:53:22.320 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:22.325 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:22.335 alo = 500, ahi = 1101
2025-07-02 05:53:22.348 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:22.357 blo = 500, bhi = 1101
2025-07-02 05:53:22.365
2025-07-02 05:53:22.371 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:22.378 g = []
2025-07-02 05:53:22.385 if alo < ahi:
2025-07-02 05:53:22.391 if blo < bhi:
2025-07-02 05:53:22.402 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:22.413 else:
2025-07-02 05:53:22.421 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:22.429 elif blo < bhi:
2025-07-02 05:53:22.435 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:22.441
2025-07-02 05:53:22.447 >       yield from g
2025-07-02 05:53:22.461
2025-07-02 05:53:22.470 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:22.479 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:22.485
2025-07-02 05:53:22.492 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:22.503 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:22.511 alo = 500, ahi = 1101
2025-07-02 05:53:22.519 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:22.526 blo = 500, bhi = 1101
2025-07-02 05:53:22.537
2025-07-02 05:53:22.549 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:22.559 r"""
2025-07-02 05:53:22.567 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:22.574 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:22.581 synch point, and intraline difference marking is done on the
2025-07-02 05:53:22.587 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:22.595
2025-07-02 05:53:22.610 Example:
2025-07-02 05:53:22.623
2025-07-02 05:53:22.636 >>> d = Differ()
2025-07-02 05:53:22.647 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:22.656 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:22.663 >>> print(''.join(results), end="")
2025-07-02 05:53:22.670 - abcDefghiJkl
2025-07-02 05:53:22.691 + abcdefGhijkl
2025-07-02 05:53:22.706 """
2025-07-02 05:53:22.713
2025-07-02 05:53:22.721 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:22.729 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:22.736 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:22.744 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:22.752 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:22.760
2025-07-02 05:53:22.771 # search for the pair that matches best without being identical
2025-07-02 05:53:22.783 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:22.791 # on junk -- unless we have to)
2025-07-02 05:53:22.798 for j in range(blo, bhi):
2025-07-02 05:53:22.806 bj = b[j]
2025-07-02 05:53:22.813 cruncher.set_seq2(bj)
2025-07-02 05:53:22.821 for i in range(alo, ahi):
2025-07-02 05:53:22.828 ai = a[i]
2025-07-02 05:53:22.836 if ai == bj:
2025-07-02 05:53:22.843 if eqi is None:
2025-07-02 05:53:22.851 eqi, eqj = i, j
2025-07-02 05:53:22.859 continue
2025-07-02 05:53:22.867 cruncher.set_seq1(ai)
2025-07-02 05:53:22.875 # computing similarity is expensive, so use the quick
2025-07-02 05:53:22.883 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:22.890 # compares by a factor of 3.
2025-07-02 05:53:22.898 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:22.909 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:22.922 # of the computation is cached by cruncher
2025-07-02 05:53:22.934 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:22.946 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:22.955 cruncher.ratio() > best_ratio:
2025-07-02 05:53:22.962 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:22.968 if best_ratio < cutoff:
2025-07-02 05:53:22.974 # no non-identical "pretty close" pair
2025-07-02 05:53:22.984 if eqi is None:
2025-07-02 05:53:22.996 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:23.007 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:23.018 return
2025-07-02 05:53:23.026 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:23.037 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:23.049 else:
2025-07-02 05:53:23.060 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:23.069 eqi = None
2025-07-02 05:53:23.076
2025-07-02 05:53:23.083 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:23.089 # identical
2025-07-02 05:53:23.095
2025-07-02 05:53:23.101 # pump out diffs from before the synch point
2025-07-02 05:53:23.108 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:23.114
2025-07-02 05:53:23.119 # do intraline marking on the synch pair
2025-07-02 05:53:23.125 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:23.133 if eqi is None:
2025-07-02 05:53:23.145 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:23.156 atags = btags = ""
2025-07-02 05:53:23.168 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:23.180 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:23.195 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:23.207 if tag == 'replace':
2025-07-02 05:53:23.216 atags += '^' * la
2025-07-02 05:53:23.223 btags += '^' * lb
2025-07-02 05:53:23.230 elif tag == 'delete':
2025-07-02 05:53:23.236 atags += '-' * la
2025-07-02 05:53:23.243 elif tag == 'insert':
2025-07-02 05:53:23.249 btags += '+' * lb
2025-07-02 05:53:23.254 elif tag == 'equal':
2025-07-02 05:53:23.260 atags += ' ' * la
2025-07-02 05:53:23.266 btags += ' ' * lb
2025-07-02 05:53:23.272 else:
2025-07-02 05:53:23.278 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:23.285 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:23.291 else:
2025-07-02 05:53:23.299 # the synch pair is identical
2025-07-02 05:53:23.311 yield '  ' + aelt
2025-07-02 05:53:23.320
2025-07-02 05:53:23.327 # pump out diffs from after the synch point
2025-07-02 05:53:23.335 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:23.346
2025-07-02 05:53:23.356 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:23.364 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:23.371
2025-07-02 05:53:23.378 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:23.385 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:23.394 alo = 501, ahi = 1101
2025-07-02 05:53:23.408 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:23.419 blo = 501, bhi = 1101
2025-07-02 05:53:23.428
2025-07-02 05:53:23.436 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:23.443 g = []
2025-07-02 05:53:23.449 if alo < ahi:
2025-07-02 05:53:23.459 if blo < bhi:
2025-07-02 05:53:23.472 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:23.480 else:
2025-07-02 05:53:23.486 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:23.498 elif blo < bhi:
2025-07-02 05:53:23.507 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:23.517
2025-07-02 05:53:23.528 >       yield from g
2025-07-02 05:53:23.536
2025-07-02 05:53:23.547 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:23.556 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:23.566
2025-07-02 05:53:23.575 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:23.584 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:23.591 alo = 501, ahi = 1101
2025-07-02 05:53:23.599 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:23.607 blo = 501, bhi = 1101
2025-07-02 05:53:23.615
2025-07-02 05:53:23.622 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:23.634 r"""
2025-07-02 05:53:23.643 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:23.654 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:23.667 synch point, and intraline difference marking is done on the
2025-07-02 05:53:23.680 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:23.693
2025-07-02 05:53:23.705 Example:
2025-07-02 05:53:23.717
2025-07-02 05:53:23.732 >>> d = Differ()
2025-07-02 05:53:23.743 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:23.750 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:23.756 >>> print(''.join(results), end="")
2025-07-02 05:53:23.763 - abcDefghiJkl
2025-07-02 05:53:23.781 + abcdefGhijkl
2025-07-02 05:53:23.797 """
2025-07-02 05:53:23.803
2025-07-02 05:53:23.817 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:23.828 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:23.837 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:23.845 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:23.851 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:23.859
2025-07-02 05:53:23.871 # search for the pair that matches best without being identical
2025-07-02 05:53:23.880 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:23.887 # on junk -- unless we have to)
2025-07-02 05:53:23.894 for j in range(blo, bhi):
2025-07-02 05:53:23.900 bj = b[j]
2025-07-02 05:53:23.906 cruncher.set_seq2(bj)
2025-07-02 05:53:23.916 for i in range(alo, ahi):
2025-07-02 05:53:23.927 ai = a[i]
2025-07-02 05:53:23.935 if ai == bj:
2025-07-02 05:53:23.942 if eqi is None:
2025-07-02 05:53:23.951 eqi, eqj = i, j
2025-07-02 05:53:23.958 continue
2025-07-02 05:53:23.965 cruncher.set_seq1(ai)
2025-07-02 05:53:23.972 # computing similarity is expensive, so use the quick
2025-07-02 05:53:23.978 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:23.988 # compares by a factor of 3.
2025-07-02 05:53:24.000 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:24.011 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:24.020 # of the computation is cached by cruncher
2025-07-02 05:53:24.028 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:24.041 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:24.053 cruncher.ratio() > best_ratio:
2025-07-02 05:53:24.067 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:24.077 if best_ratio < cutoff:
2025-07-02 05:53:24.089 # no non-identical "pretty close" pair
2025-07-02 05:53:24.100 if eqi is None:
2025-07-02 05:53:24.109 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:24.122 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:24.134 return
2025-07-02 05:53:24.142 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:24.150 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:24.156 else:
2025-07-02 05:53:24.162 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:24.168 eqi = None
2025-07-02 05:53:24.182
2025-07-02 05:53:24.193 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:24.206 # identical
2025-07-02 05:53:24.217
2025-07-02 05:53:24.228 # pump out diffs from before the synch point
2025-07-02 05:53:24.238 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:24.252
2025-07-02 05:53:24.263 # do intraline marking on the synch pair
2025-07-02 05:53:24.273 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:24.281 if eqi is None:
2025-07-02 05:53:24.291 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:24.303 atags = btags = ""
2025-07-02 05:53:24.313 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:24.321 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:24.329 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:24.335 if tag == 'replace':
2025-07-02 05:53:24.343 atags += '^' * la
2025-07-02 05:53:24.354 btags += '^' * lb
2025-07-02 05:53:24.367 elif tag == 'delete':
2025-07-02 05:53:24.378 atags += '-' * la
2025-07-02 05:53:24.387 elif tag == 'insert':
2025-07-02 05:53:24.397 btags += '+' * lb
2025-07-02 05:53:24.407 elif tag == 'equal':
2025-07-02 05:53:24.419 atags += ' ' * la
2025-07-02 05:53:24.433 btags += ' ' * lb
2025-07-02 05:53:24.445 else:
2025-07-02 05:53:24.456 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:24.466 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:24.479 else:
2025-07-02 05:53:24.492 # the synch pair is identical
2025-07-02 05:53:24.503 yield '  ' + aelt
2025-07-02 05:53:24.511
2025-07-02 05:53:24.518 # pump out diffs from after the synch point
2025-07-02 05:53:24.527 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:24.541
2025-07-02 05:53:24.552 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:24.560 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:24.566
2025-07-02 05:53:24.573 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:24.583 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:24.589 alo = 502, ahi = 1101
2025-07-02 05:53:24.597 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:24.602 blo = 502, bhi = 1101
2025-07-02 05:53:24.608
2025-07-02 05:53:24.615 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:24.622 g = []
2025-07-02 05:53:24.628 if alo < ahi:
2025-07-02 05:53:24.634 if blo < bhi:
2025-07-02 05:53:24.640 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:24.646 else:
2025-07-02 05:53:24.653 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:24.658 elif blo < bhi:
2025-07-02 05:53:24.664 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:24.671
2025-07-02 05:53:24.678 >       yield from g
2025-07-02 05:53:24.687
2025-07-02 05:53:24.700 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:24.709 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:24.720
2025-07-02 05:53:24.729 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:24.742 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:24.756 alo = 502, ahi = 1101
2025-07-02 05:53:24.767 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:24.778 blo = 502, bhi = 1101
2025-07-02 05:53:24.787
2025-07-02 05:53:24.797 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:24.805 r"""
2025-07-02 05:53:24.812 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:24.819 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:24.827 synch point, and intraline difference marking is done on the
2025-07-02 05:53:24.838 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:24.846
2025-07-02 05:53:24.857 Example:
2025-07-02 05:53:24.871
2025-07-02 05:53:24.883 >>> d = Differ()
2025-07-02 05:53:24.894 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:24.903 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:24.910 >>> print(''.join(results), end="")
2025-07-02 05:53:24.917 - abcDefghiJkl
2025-07-02 05:53:24.931 + abcdefGhijkl
2025-07-02 05:53:24.950 """
2025-07-02 05:53:24.958
2025-07-02 05:53:24.966 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:24.975 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:24.985 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:25.001 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:25.013 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:25.026
2025-07-02 05:53:25.040 # search for the pair that matches best without being identical
2025-07-02 05:53:25.048 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:25.055 # on junk -- unless we have to)
2025-07-02 05:53:25.064 for j in range(blo, bhi):
2025-07-02 05:53:25.072 bj = b[j]
2025-07-02 05:53:25.081 cruncher.set_seq2(bj)
2025-07-02 05:53:25.090 for i in range(alo, ahi):
2025-07-02 05:53:25.096 ai = a[i]
2025-07-02 05:53:25.103 if ai == bj:
2025-07-02 05:53:25.114 if eqi is None:
2025-07-02 05:53:25.124 eqi, eqj = i, j
2025-07-02 05:53:25.132 continue
2025-07-02 05:53:25.139 cruncher.set_seq1(ai)
2025-07-02 05:53:25.146 # computing similarity is expensive, so use the quick
2025-07-02 05:53:25.160 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:25.170 # compares by a factor of 3.
2025-07-02 05:53:25.183 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:25.194 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:25.206 # of the computation is cached by cruncher
2025-07-02 05:53:25.218 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:25.228 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:25.238 cruncher.ratio() > best_ratio:
2025-07-02 05:53:25.251 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:25.259 if best_ratio < cutoff:
2025-07-02 05:53:25.266 # no non-identical "pretty close" pair
2025-07-02 05:53:25.272 if eqi is None:
2025-07-02 05:53:25.277 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:25.284 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:25.293 return
2025-07-02 05:53:25.306 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:25.316 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:25.324 else:
2025-07-02 05:53:25.332 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:25.338 eqi = None
2025-07-02 05:53:25.350
2025-07-02 05:53:25.363 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:25.371 # identical
2025-07-02 05:53:25.384
2025-07-02 05:53:25.396 # pump out diffs from before the synch point
2025-07-02 05:53:25.409 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:25.418
2025-07-02 05:53:25.429 # do intraline marking on the synch pair
2025-07-02 05:53:25.437 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:25.449 if eqi is None:
2025-07-02 05:53:25.463 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:25.470 atags = btags = ""
2025-07-02 05:53:25.480 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:25.492 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:25.507 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:25.516 if tag == 'replace':
2025-07-02 05:53:25.528 atags += '^' * la
2025-07-02 05:53:25.539 btags += '^' * lb
2025-07-02 05:53:25.551 elif tag == 'delete':
2025-07-02 05:53:25.561 atags += '-' * la
2025-07-02 05:53:25.572 elif tag == 'insert':
2025-07-02 05:53:25.584 btags += '+' * lb
2025-07-02 05:53:25.596 elif tag == 'equal':
2025-07-02 05:53:25.604 atags += ' ' * la
2025-07-02 05:53:25.611 btags += ' ' * lb
2025-07-02 05:53:25.617 else:
2025-07-02 05:53:25.622 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:25.628 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:25.634 else:
2025-07-02 05:53:25.640 # the synch pair is identical
2025-07-02 05:53:25.647 yield '  ' + aelt
2025-07-02 05:53:25.653
2025-07-02 05:53:25.659 # pump out diffs from after the synch point
2025-07-02 05:53:25.666 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:25.676
2025-07-02 05:53:25.686 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:25.694 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:25.701
2025-07-02 05:53:25.707 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:25.715 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:25.721 alo = 503, ahi = 1101
2025-07-02 05:53:25.731 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:25.739 blo = 503, bhi = 1101
2025-07-02 05:53:25.749
2025-07-02 05:53:25.758 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:25.765 g = []
2025-07-02 05:53:25.771 if alo < ahi:
2025-07-02 05:53:25.777 if blo < bhi:
2025-07-02 05:53:25.783 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:25.789 else:
2025-07-02 05:53:25.795 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:25.801 elif blo < bhi:
2025-07-02 05:53:25.808 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:25.815
2025-07-02 05:53:25.822 >       yield from g
2025-07-02 05:53:25.829
2025-07-02 05:53:25.841 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:25.851 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:25.859
2025-07-02 05:53:25.865 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:25.870 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:25.876 alo = 503, ahi = 1101
2025-07-02 05:53:25.882 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:25.889 blo = 503, bhi = 1101
2025-07-02 05:53:25.895
2025-07-02 05:53:25.902 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:25.909 r"""
2025-07-02 05:53:25.916 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:25.924 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:25.931 synch point, and intraline difference marking is done on the
2025-07-02 05:53:25.937 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:25.943
2025-07-02 05:53:25.949 Example:
2025-07-02 05:53:25.955
2025-07-02 05:53:25.961 >>> d = Differ()
2025-07-02 05:53:25.967 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:25.973 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:25.979 >>> print(''.join(results), end="")
2025-07-02 05:53:25.985 - abcDefghiJkl
2025-07-02 05:53:25.997 + abcdefGhijkl
2025-07-02 05:53:26.011 """
2025-07-02 05:53:26.018
2025-07-02 05:53:26.024 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:26.030 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:26.037 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:26.044 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:26.051 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:26.064
2025-07-02 05:53:26.076 # search for the pair that matches best without being identical
2025-07-02 05:53:26.084 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:26.090 # on junk -- unless we have to)
2025-07-02 05:53:26.096 for j in range(blo, bhi):
2025-07-02 05:53:26.102 bj = b[j]
2025-07-02 05:53:26.108 cruncher.set_seq2(bj)
2025-07-02 05:53:26.114 for i in range(alo, ahi):
2025-07-02 05:53:26.120 ai = a[i]
2025-07-02 05:53:26.127 if ai == bj:
2025-07-02 05:53:26.136 if eqi is None:
2025-07-02 05:53:26.143 eqi, eqj = i, j
2025-07-02 05:53:26.151 continue
2025-07-02 05:53:26.158 cruncher.set_seq1(ai)
2025-07-02 05:53:26.167 # computing similarity is expensive, so use the quick
2025-07-02 05:53:26.174 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:26.180 # compares by a factor of 3.
2025-07-02 05:53:26.186 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:26.191 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:26.197 # of the computation is cached by cruncher
2025-07-02 05:53:26.203 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:26.209 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:26.216 cruncher.ratio() > best_ratio:
2025-07-02 05:53:26.224 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:26.231 if best_ratio < cutoff:
2025-07-02 05:53:26.238 # no non-identical "pretty close" pair
2025-07-02 05:53:26.245 if eqi is None:
2025-07-02 05:53:26.252 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:26.263 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:26.272 return
2025-07-02 05:53:26.279 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:26.286 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:26.292 else:
2025-07-02 05:53:26.299 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:26.306 eqi = None
2025-07-02 05:53:26.317
2025-07-02 05:53:26.327 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:26.335 # identical
2025-07-02 05:53:26.341
2025-07-02 05:53:26.346 # pump out diffs from before the synch point
2025-07-02 05:53:26.351 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:26.356
2025-07-02 05:53:26.360 # do intraline marking on the synch pair
2025-07-02 05:53:26.365 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:26.371 if eqi is None:
2025-07-02 05:53:26.378 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:26.384 atags = btags = ""
2025-07-02 05:53:26.391 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:26.402 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:26.410 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:26.416 if tag == 'replace':
2025-07-02 05:53:26.423 atags += '^' * la
2025-07-02 05:53:26.428 btags += '^' * lb
2025-07-02 05:53:26.434 elif tag == 'delete':
2025-07-02 05:53:26.444 atags += '-' * la
2025-07-02 05:53:26.453 elif tag == 'insert':
2025-07-02 05:53:26.460 btags += '+' * lb
2025-07-02 05:53:26.467 elif tag == 'equal':
2025-07-02 05:53:26.474 atags += ' ' * la
2025-07-02 05:53:26.487 btags += ' ' * lb
2025-07-02 05:53:26.496 else:
2025-07-02 05:53:26.503 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:26.510 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:26.517 else:
2025-07-02 05:53:26.525 # the synch pair is identical
2025-07-02 05:53:26.531 yield '  ' + aelt
2025-07-02 05:53:26.538
2025-07-02 05:53:26.548 # pump out diffs from after the synch point
2025-07-02 05:53:26.558 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:26.566
2025-07-02 05:53:26.574 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:26.585 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:26.593
2025-07-02 05:53:26.606 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:26.618 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:26.626 alo = 504, ahi = 1101
2025-07-02 05:53:26.634 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:26.641 blo = 504, bhi = 1101
2025-07-02 05:53:26.646
2025-07-02 05:53:26.652 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:26.658 g = []
2025-07-02 05:53:26.663 if alo < ahi:
2025-07-02 05:53:26.671 if blo < bhi:
2025-07-02 05:53:26.682 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:26.689 else:
2025-07-02 05:53:26.696 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:26.703 elif blo < bhi:
2025-07-02 05:53:26.710 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:26.716
2025-07-02 05:53:26.722 >       yield from g
2025-07-02 05:53:26.731
2025-07-02 05:53:26.739 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:26.751 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:26.761
2025-07-02 05:53:26.769 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:26.781 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:26.792 alo = 504, ahi = 1101
2025-07-02 05:53:26.800 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:26.807 blo = 504, bhi = 1101
2025-07-02 05:53:26.814
2025-07-02 05:53:26.825 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:26.833 r"""
2025-07-02 05:53:26.840 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:26.847 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:26.854 synch point, and intraline difference marking is done on the
2025-07-02 05:53:26.865 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:26.876
2025-07-02 05:53:26.884 Example:
2025-07-02 05:53:26.891
2025-07-02 05:53:26.903 >>> d = Differ()
2025-07-02 05:53:26.915 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:26.925 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:26.936 >>> print(''.join(results), end="")
2025-07-02 05:53:26.946 - abcDefghiJkl
2025-07-02 05:53:26.967 + abcdefGhijkl
2025-07-02 05:53:26.992 """
2025-07-02 05:53:27.001
2025-07-02 05:53:27.009 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:27.021 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:27.031 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:27.039 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:27.052 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:27.064
2025-07-02 05:53:27.078 # search for the pair that matches best without being identical
2025-07-02 05:53:27.090 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:27.101 # on junk -- unless we have to)
2025-07-02 05:53:27.114 for j in range(blo, bhi):
2025-07-02 05:53:27.124 bj = b[j]
2025-07-02 05:53:27.135 cruncher.set_seq2(bj)
2025-07-02 05:53:27.145 for i in range(alo, ahi):
2025-07-02 05:53:27.158 ai = a[i]
2025-07-02 05:53:27.169 if ai == bj:
2025-07-02 05:53:27.176 if eqi is None:
2025-07-02 05:53:27.183 eqi, eqj = i, j
2025-07-02 05:53:27.189 continue
2025-07-02 05:53:27.195 cruncher.set_seq1(ai)
2025-07-02 05:53:27.204 # computing similarity is expensive, so use the quick
2025-07-02 05:53:27.218 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:27.231 # compares by a factor of 3.
2025-07-02 05:53:27.243 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:27.253 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:27.265 # of the computation is cached by cruncher
2025-07-02 05:53:27.275 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:27.287 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:27.300 cruncher.ratio() > best_ratio:
2025-07-02 05:53:27.308 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:27.315 if best_ratio < cutoff:
2025-07-02 05:53:27.321 # no non-identical "pretty close" pair
2025-07-02 05:53:27.327 if eqi is None:
2025-07-02 05:53:27.333 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:27.339 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:27.345 return
2025-07-02 05:53:27.358 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:27.372 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:27.383 else:
2025-07-02 05:53:27.390 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:27.401 eqi = None
2025-07-02 05:53:27.410
2025-07-02 05:53:27.418 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:27.424 # identical
2025-07-02 05:53:27.431
2025-07-02 05:53:27.439 # pump out diffs from before the synch point
2025-07-02 05:53:27.447 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:27.453
2025-07-02 05:53:27.459 # do intraline marking on the synch pair
2025-07-02 05:53:27.464 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:27.470 if eqi is None:
2025-07-02 05:53:27.475 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:27.480 atags = btags = ""
2025-07-02 05:53:27.493 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:27.505 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:27.517 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:27.525 if tag == 'replace':
2025-07-02 05:53:27.531 atags += '^' * la
2025-07-02 05:53:27.537 btags += '^' * lb
2025-07-02 05:53:27.549 elif tag == 'delete':
2025-07-02 05:53:27.560 atags += '-' * la
2025-07-02 05:53:27.568 elif tag == 'insert':
2025-07-02 05:53:27.575 btags += '+' * lb
2025-07-02 05:53:27.581 elif tag == 'equal':
2025-07-02 05:53:27.587 atags += ' ' * la
2025-07-02 05:53:27.601 btags += ' ' * lb
2025-07-02 05:53:27.612 else:
2025-07-02 05:53:27.625 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:27.636 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:27.648 else:
2025-07-02 05:53:27.661 # the synch pair is identical
2025-07-02 05:53:27.672 yield '  ' + aelt
2025-07-02 05:53:27.681
2025-07-02 05:53:27.689 # pump out diffs from after the synch point
2025-07-02 05:53:27.696 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:27.702
2025-07-02 05:53:27.708 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:27.715 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:27.721
2025-07-02 05:53:27.728 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:27.736 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:27.744 alo = 505, ahi = 1101
2025-07-02 05:53:27.756 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:27.763 blo = 505, bhi = 1101
2025-07-02 05:53:27.771
2025-07-02 05:53:27.782 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:27.790 g = []
2025-07-02 05:53:27.797 if alo < ahi:
2025-07-02 05:53:27.807 if blo < bhi:
2025-07-02 05:53:27.818 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:27.838 else:
2025-07-02 05:53:27.849 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:27.858 elif blo < bhi:
2025-07-02 05:53:27.871 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:27.882
2025-07-02 05:53:27.894 >       yield from g
2025-07-02 05:53:27.904
2025-07-02 05:53:27.913 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:27.920 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:27.926
2025-07-02 05:53:27.931 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:27.939 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:27.948 alo = 505, ahi = 1101
2025-07-02 05:53:27.953 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:27.958 blo = 505, bhi = 1101
2025-07-02 05:53:27.966
2025-07-02 05:53:27.975 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:27.981 r"""
2025-07-02 05:53:27.987 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:27.993 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:27.999 synch point, and intraline difference marking is done on the
2025-07-02 05:53:28.007 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:28.018
2025-07-02 05:53:28.027 Example:
2025-07-02 05:53:28.036
2025-07-02 05:53:28.043 >>> d = Differ()
2025-07-02 05:53:28.050 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:28.058 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:28.069 >>> print(''.join(results), end="")
2025-07-02 05:53:28.080 - abcDefghiJkl
2025-07-02 05:53:28.096 + abcdefGhijkl
2025-07-02 05:53:28.108 """
2025-07-02 05:53:28.115
2025-07-02 05:53:28.121 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:28.133 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:28.142 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:28.151 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:28.158 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:28.168
2025-07-02 05:53:28.178 # search for the pair that matches best without being identical
2025-07-02 05:53:28.187 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:28.199 # on junk -- unless we have to)
2025-07-02 05:53:28.208 for j in range(blo, bhi):
2025-07-02 05:53:28.216 bj = b[j]
2025-07-02 05:53:28.223 cruncher.set_seq2(bj)
2025-07-02 05:53:28.231 for i in range(alo, ahi):
2025-07-02 05:53:28.239 ai = a[i]
2025-07-02 05:53:28.250 if ai == bj:
2025-07-02 05:53:28.258 if eqi is None:
2025-07-02 05:53:28.265 eqi, eqj = i, j
2025-07-02 05:53:28.270 continue
2025-07-02 05:53:28.275 cruncher.set_seq1(ai)
2025-07-02 05:53:28.282 # computing similarity is expensive, so use the quick
2025-07-02 05:53:28.288 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:28.294 # compares by a factor of 3.
2025-07-02 05:53:28.302 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:28.310 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:28.315 # of the computation is cached by cruncher
2025-07-02 05:53:28.320 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:28.325 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:28.333 cruncher.ratio() > best_ratio:
2025-07-02 05:53:28.341 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:28.348 if best_ratio < cutoff:
2025-07-02 05:53:28.353 # no non-identical "pretty close" pair
2025-07-02 05:53:28.359 if eqi is None:
2025-07-02 05:53:28.366 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:28.377 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:28.387 return
2025-07-02 05:53:28.395 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:28.401 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:28.408 else:
2025-07-02 05:53:28.413 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:28.420 eqi = None
2025-07-02 05:53:28.426
2025-07-02 05:53:28.432 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:28.438 # identical
2025-07-02 05:53:28.447
2025-07-02 05:53:28.456 # pump out diffs from before the synch point
2025-07-02 05:53:28.464 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:28.471
2025-07-02 05:53:28.478 # do intraline marking on the synch pair
2025-07-02 05:53:28.492 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:28.503 if eqi is None:
2025-07-02 05:53:28.513 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:28.522 atags = btags = ""
2025-07-02 05:53:28.528 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:28.540 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:28.553 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:28.565 if tag == 'replace':
2025-07-02 05:53:28.576 atags += '^' * la
2025-07-02 05:53:28.589 btags += '^' * lb
2025-07-02 05:53:28.600 elif tag == 'delete':
2025-07-02 05:53:28.610 atags += '-' * la
2025-07-02 05:53:28.617 elif tag == 'insert':
2025-07-02 05:53:28.623 btags += '+' * lb
2025-07-02 05:53:28.628 elif tag == 'equal':
2025-07-02 05:53:28.633 atags += ' ' * la
2025-07-02 05:53:28.638 btags += ' ' * lb
2025-07-02 05:53:28.643 else:
2025-07-02 05:53:28.648 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:28.656 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:28.664 else:
2025-07-02 05:53:28.672 # the synch pair is identical
2025-07-02 05:53:28.678 yield '  ' + aelt
2025-07-02 05:53:28.689
2025-07-02 05:53:28.700 # pump out diffs from after the synch point
2025-07-02 05:53:28.709 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:28.719
2025-07-02 05:53:28.731 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:28.742 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:28.752
2025-07-02 05:53:28.761 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:28.769 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:28.775 alo = 506, ahi = 1101
2025-07-02 05:53:28.784 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:28.790 blo = 506, bhi = 1101
2025-07-02 05:53:28.796
2025-07-02 05:53:28.803 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:28.811 g = []
2025-07-02 05:53:28.820 if alo < ahi:
2025-07-02 05:53:28.828 if blo < bhi:
2025-07-02 05:53:28.836 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:28.843 else:
2025-07-02 05:53:28.851 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:28.859 elif blo < bhi:
2025-07-02 05:53:28.870 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:28.877
2025-07-02 05:53:28.884 >       yield from g
2025-07-02 05:53:28.890
2025-07-02 05:53:28.901 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:28.912 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:28.921
2025-07-02 05:53:28.930 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:28.938 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:28.946 alo = 506, ahi = 1101
2025-07-02 05:53:28.953 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:28.958 blo = 506, bhi = 1101
2025-07-02 05:53:28.963
2025-07-02 05:53:28.968 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:28.973 r"""
2025-07-02 05:53:28.978 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:28.985 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:28.991 synch point, and intraline difference marking is done on the
2025-07-02 05:53:28.997 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:29.003
2025-07-02 05:53:29.011 Example:
2025-07-02 05:53:29.021
2025-07-02 05:53:29.029 >>> d = Differ()
2025-07-02 05:53:29.036 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:29.043 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:29.051 >>> print(''.join(results), end="")
2025-07-02 05:53:29.061 - abcDefghiJkl
2025-07-02 05:53:29.076 + abcdefGhijkl
2025-07-02 05:53:29.094 """
2025-07-02 05:53:29.104
2025-07-02 05:53:29.112 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:29.120 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:29.127 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:29.135 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:29.145 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:29.154
2025-07-02 05:53:29.161 # search for the pair that matches best without being identical
2025-07-02 05:53:29.167 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:29.174 # on junk -- unless we have to)
2025-07-02 05:53:29.186 for j in range(blo, bhi):
2025-07-02 05:53:29.196 bj = b[j]
2025-07-02 05:53:29.203 cruncher.set_seq2(bj)
2025-07-02 05:53:29.210 for i in range(alo, ahi):
2025-07-02 05:53:29.220 ai = a[i]
2025-07-02 05:53:29.229 if ai == bj:
2025-07-02 05:53:29.239 if eqi is None:
2025-07-02 05:53:29.248 eqi, eqj = i, j
2025-07-02 05:53:29.256 continue
2025-07-02 05:53:29.263 cruncher.set_seq1(ai)
2025-07-02 05:53:29.271 # computing similarity is expensive, so use the quick
2025-07-02 05:53:29.278 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:29.284 # compares by a factor of 3.
2025-07-02 05:53:29.293 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:29.301 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:29.308 # of the computation is cached by cruncher
2025-07-02 05:53:29.315 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:29.324 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:29.331 cruncher.ratio() > best_ratio:
2025-07-02 05:53:29.339 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:29.347 if best_ratio < cutoff:
2025-07-02 05:53:29.359 # no non-identical "pretty close" pair
2025-07-02 05:53:29.368 if eqi is None:
2025-07-02 05:53:29.379 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:29.388 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:29.395 return
2025-07-02 05:53:29.402 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:29.409 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:29.415 else:
2025-07-02 05:53:29.423 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:29.435 eqi = None
2025-07-02 05:53:29.443
2025-07-02 05:53:29.450 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:29.461 # identical
2025-07-02 05:53:29.470
2025-07-02 05:53:29.477 # pump out diffs from before the synch point
2025-07-02 05:53:29.489 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:29.498
2025-07-02 05:53:29.507 # do intraline marking on the synch pair
2025-07-02 05:53:29.515 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:29.523 if eqi is None:
2025-07-02 05:53:29.530 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:29.537 atags = btags = ""
2025-07-02 05:53:29.544 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:29.550 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:29.559 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:29.573 if tag == 'replace':
2025-07-02 05:53:29.582 atags += '^' * la
2025-07-02 05:53:29.590 btags += '^' * lb
2025-07-02 05:53:29.600 elif tag == 'delete':
2025-07-02 05:53:29.611 atags += '-' * la
2025-07-02 05:53:29.621 elif tag == 'insert':
2025-07-02 05:53:29.633 btags += '+' * lb
2025-07-02 05:53:29.643 elif tag == 'equal':
2025-07-02 05:53:29.650 atags += ' ' * la
2025-07-02 05:53:29.657 btags += ' ' * lb
2025-07-02 05:53:29.663 else:
2025-07-02 05:53:29.669 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:29.676 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:29.683 else:
2025-07-02 05:53:29.694 # the synch pair is identical
2025-07-02 05:53:29.701 yield '  ' + aelt
2025-07-02 05:53:29.706
2025-07-02 05:53:29.714 # pump out diffs from after the synch point
2025-07-02 05:53:29.724 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:29.732
2025-07-02 05:53:29.740 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:29.747 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:29.753
2025-07-02 05:53:29.761 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:29.768 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:29.774 alo = 507, ahi = 1101
2025-07-02 05:53:29.783 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:29.791 blo = 507, bhi = 1101
2025-07-02 05:53:29.798
2025-07-02 05:53:29.807 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:29.818 g = []
2025-07-02 05:53:29.829 if alo < ahi:
2025-07-02 05:53:29.839 if blo < bhi:
2025-07-02 05:53:29.852 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:29.864 else:
2025-07-02 05:53:29.878 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:29.887 elif blo < bhi:
2025-07-02 05:53:29.899 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:29.909
2025-07-02 05:53:29.916 >       yield from g
2025-07-02 05:53:29.923
2025-07-02 05:53:29.929 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:29.935 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:29.941
2025-07-02 05:53:29.947 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:29.955 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:29.961 alo = 507, ahi = 1101
2025-07-02 05:53:29.968 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:29.974 blo = 507, bhi = 1101
2025-07-02 05:53:29.979
2025-07-02 05:53:29.987 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:29.998 r"""
2025-07-02 05:53:30.007 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:30.014 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:30.025 synch point, and intraline difference marking is done on the
2025-07-02 05:53:30.036 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:30.045
2025-07-02 05:53:30.053 Example:
2025-07-02 05:53:30.060
2025-07-02 05:53:30.069 >>> d = Differ()
2025-07-02 05:53:30.078 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:30.086 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:30.097 >>> print(''.join(results), end="")
2025-07-02 05:53:30.107 - abcDefghiJkl
2025-07-02 05:53:30.124 + abcdefGhijkl
2025-07-02 05:53:30.143 """
2025-07-02 05:53:30.151
2025-07-02 05:53:30.159 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:30.165 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:30.170 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:30.176 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:30.182 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:30.193
2025-07-02 05:53:30.203 # search for the pair that matches best without being identical
2025-07-02 05:53:30.211 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:30.219 # on junk -- unless we have to)
2025-07-02 05:53:30.231 for j in range(blo, bhi):
2025-07-02 05:53:30.239 bj = b[j]
2025-07-02 05:53:30.247 cruncher.set_seq2(bj)
2025-07-02 05:53:30.255 for i in range(alo, ahi):
2025-07-02 05:53:30.266 ai = a[i]
2025-07-02 05:53:30.274 if ai == bj:
2025-07-02 05:53:30.281 if eqi is None:
2025-07-02 05:53:30.288 eqi, eqj = i, j
2025-07-02 05:53:30.301 continue
2025-07-02 05:53:30.311 cruncher.set_seq1(ai)
2025-07-02 05:53:30.319 # computing similarity is expensive, so use the quick
2025-07-02 05:53:30.327 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:30.334 # compares by a factor of 3.
2025-07-02 05:53:30.340 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:30.346 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:30.356 # of the computation is cached by cruncher
2025-07-02 05:53:30.367 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:30.376 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:30.385 cruncher.ratio() > best_ratio:
2025-07-02 05:53:30.392 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:30.398 if best_ratio < cutoff:
2025-07-02 05:53:30.405 # no non-identical "pretty close" pair
2025-07-02 05:53:30.410 if eqi is None:
2025-07-02 05:53:30.417 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:30.423 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:30.430 return
2025-07-02 05:53:30.438 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:30.447 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:30.457 else:
2025-07-02 05:53:30.466 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:30.473 eqi = None
2025-07-02 05:53:30.479
2025-07-02 05:53:30.485 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:30.491 # identical
2025-07-02 05:53:30.497
2025-07-02 05:53:30.506 # pump out diffs from before the synch point
2025-07-02 05:53:30.517 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:30.526
2025-07-02 05:53:30.534 # do intraline marking on the synch pair
2025-07-02 05:53:30.542 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:30.554 if eqi is None:
2025-07-02 05:53:30.563 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:30.573 atags = btags = ""
2025-07-02 05:53:30.587 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:30.601 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:30.612 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:30.621 if tag == 'replace':
2025-07-02 05:53:30.629 atags += '^' * la
2025-07-02 05:53:30.636 btags += '^' * lb
2025-07-02 05:53:30.644 elif tag == 'delete':
2025-07-02 05:53:30.656 atags += '-' * la
2025-07-02 05:53:30.664 elif tag == 'insert':
2025-07-02 05:53:30.672 btags += '+' * lb
2025-07-02 05:53:30.678 elif tag == 'equal':
2025-07-02 05:53:30.684 atags += ' ' * la
2025-07-02 05:53:30.692 btags += ' ' * lb
2025-07-02 05:53:30.705 else:
2025-07-02 05:53:30.718 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:30.728 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:30.739 else:
2025-07-02 05:53:30.748 # the synch pair is identical
2025-07-02 05:53:30.755 yield '  ' + aelt
2025-07-02 05:53:30.761
2025-07-02 05:53:30.767 # pump out diffs from after the synch point
2025-07-02 05:53:30.778 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:30.787
2025-07-02 05:53:30.795 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:30.801 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:30.806
2025-07-02 05:53:30.812 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:30.819 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:30.824 alo = 510, ahi = 1101
2025-07-02 05:53:30.830 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:30.835 blo = 510, bhi = 1101
2025-07-02 05:53:30.840
2025-07-02 05:53:30.846 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:30.853 g = []
2025-07-02 05:53:30.864 if alo < ahi:
2025-07-02 05:53:30.872 if blo < bhi:
2025-07-02 05:53:30.879 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:30.886 else:
2025-07-02 05:53:30.894 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:30.901 elif blo < bhi:
2025-07-02 05:53:30.907 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:30.913
2025-07-02 05:53:30.919 >       yield from g
2025-07-02 05:53:30.924
2025-07-02 05:53:30.929 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:30.934 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:30.943
2025-07-02 05:53:30.953 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:30.961 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:30.967 alo = 510, ahi = 1101
2025-07-02 05:53:30.974 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:30.987 blo = 510, bhi = 1101
2025-07-02 05:53:30.997
2025-07-02 05:53:31.004 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:31.011 r"""
2025-07-02 05:53:31.019 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:31.027 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:31.036 synch point, and intraline difference marking is done on the
2025-07-02 05:53:31.043 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:31.050
2025-07-02 05:53:31.062 Example:
2025-07-02 05:53:31.070
2025-07-02 05:53:31.076 >>> d = Differ()
2025-07-02 05:53:31.082 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:31.088 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:31.096 >>> print(''.join(results), end="")
2025-07-02 05:53:31.108 - abcDefghiJkl
2025-07-02 05:53:31.134 + abcdefGhijkl
2025-07-02 05:53:31.158 """
2025-07-02 05:53:31.168
2025-07-02 05:53:31.180 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:31.191 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:31.200 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:31.208 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:31.214 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:31.227
2025-07-02 05:53:31.240 # search for the pair that matches best without being identical
2025-07-02 05:53:31.250 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:31.262 # on junk -- unless we have to)
2025-07-02 05:53:31.273 for j in range(blo, bhi):
2025-07-02 05:53:31.286 bj = b[j]
2025-07-02 05:53:31.296 cruncher.set_seq2(bj)
2025-07-02 05:53:31.304 for i in range(alo, ahi):
2025-07-02 05:53:31.310 ai = a[i]
2025-07-02 05:53:31.317 if ai == bj:
2025-07-02 05:53:31.323 if eqi is None:
2025-07-02 05:53:31.336 eqi, eqj = i, j
2025-07-02 05:53:31.344 continue
2025-07-02 05:53:31.352 cruncher.set_seq1(ai)
2025-07-02 05:53:31.360 # computing similarity is expensive, so use the quick
2025-07-02 05:53:31.367 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:31.375 # compares by a factor of 3.
2025-07-02 05:53:31.386 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:31.395 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:31.408 # of the computation is cached by cruncher
2025-07-02 05:53:31.416 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:31.422 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:31.428 cruncher.ratio() > best_ratio:
2025-07-02 05:53:31.432 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:31.437 if best_ratio < cutoff:
2025-07-02 05:53:31.442 # no non-identical "pretty close" pair
2025-07-02 05:53:31.446 if eqi is None:
2025-07-02 05:53:31.451 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:31.456 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:31.461 return
2025-07-02 05:53:31.466 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:31.471 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:31.475 else:
2025-07-02 05:53:31.481 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:31.486 eqi = None
2025-07-02 05:53:31.490
2025-07-02 05:53:31.495 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:31.499 # identical
2025-07-02 05:53:31.504
2025-07-02 05:53:31.509 # pump out diffs from before the synch point
2025-07-02 05:53:31.513 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:31.518
2025-07-02 05:53:31.523 # do intraline marking on the synch pair
2025-07-02 05:53:31.528 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:31.532 if eqi is None:
2025-07-02 05:53:31.537 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:31.541 atags = btags = ""
2025-07-02 05:53:31.546 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:31.551 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:31.555 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:31.560 if tag == 'replace':
2025-07-02 05:53:31.564 atags += '^' * la
2025-07-02 05:53:31.569 btags += '^' * lb
2025-07-02 05:53:31.574 elif tag == 'delete':
2025-07-02 05:53:31.578 atags += '-' * la
2025-07-02 05:53:31.583 elif tag == 'insert':
2025-07-02 05:53:31.587 btags += '+' * lb
2025-07-02 05:53:31.592 elif tag == 'equal':
2025-07-02 05:53:31.596 atags += ' ' * la
2025-07-02 05:53:31.601 btags += ' ' * lb
2025-07-02 05:53:31.605 else:
2025-07-02 05:53:31.610 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:31.614 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:31.619 else:
2025-07-02 05:53:31.629 # the synch pair is identical
2025-07-02 05:53:31.640 yield '  ' + aelt
2025-07-02 05:53:31.647
2025-07-02 05:53:31.655 # pump out diffs from after the synch point
2025-07-02 05:53:31.663 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:31.670
2025-07-02 05:53:31.683 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:31.697 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:31.708
2025-07-02 05:53:31.716 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:31.728 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:31.740 alo = 511, ahi = 1101
2025-07-02 05:53:31.752 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:31.763 blo = 511, bhi = 1101
2025-07-02 05:53:31.772
2025-07-02 05:53:31.780 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:31.787 g = []
2025-07-02 05:53:31.795 if alo < ahi:
2025-07-02 05:53:31.805 if blo < bhi:
2025-07-02 05:53:31.814 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:31.821 else:
2025-07-02 05:53:31.833 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:31.855 elif blo < bhi:
2025-07-02 05:53:31.864 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:31.872
2025-07-02 05:53:31.879 >       yield from g
2025-07-02 05:53:31.885
2025-07-02 05:53:31.890 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:31.895 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:31.900
2025-07-02 05:53:31.905 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:31.911 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:31.920 alo = 511, ahi = 1101
2025-07-02 05:53:31.933 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:31.942 blo = 511, bhi = 1101
2025-07-02 05:53:31.951
2025-07-02 05:53:31.961 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:31.969 r"""
2025-07-02 05:53:31.980 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:31.992 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:32.005 synch point, and intraline difference marking is done on the
2025-07-02 05:53:32.016 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:32.025
2025-07-02 05:53:32.038 Example:
2025-07-02 05:53:32.047
2025-07-02 05:53:32.055 >>> d = Differ()
2025-07-02 05:53:32.062 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:32.068 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:32.074 >>> print(''.join(results), end="")
2025-07-02 05:53:32.080 - abcDefghiJkl
2025-07-02 05:53:32.091 + abcdefGhijkl
2025-07-02 05:53:32.114 """
2025-07-02 05:53:32.121
2025-07-02 05:53:32.128 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:32.135 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:32.141 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:32.147 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:32.155 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:32.167
2025-07-02 05:53:32.178 # search for the pair that matches best without being identical
2025-07-02 05:53:32.189 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:32.199 # on junk -- unless we have to)
2025-07-02 05:53:32.207 for j in range(blo, bhi):
2025-07-02 05:53:32.214 bj = b[j]
2025-07-02 05:53:32.221 cruncher.set_seq2(bj)
2025-07-02 05:53:32.230 for i in range(alo, ahi):
2025-07-02 05:53:32.240 ai = a[i]
2025-07-02 05:53:32.249 if ai == bj:
2025-07-02 05:53:32.256 if eqi is None:
2025-07-02 05:53:32.263 eqi, eqj = i, j
2025-07-02 05:53:32.268 continue
2025-07-02 05:53:32.274 cruncher.set_seq1(ai)
2025-07-02 05:53:32.286 # computing similarity is expensive, so use the quick
2025-07-02 05:53:32.297 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:32.306 # compares by a factor of 3.
2025-07-02 05:53:32.315 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:32.327 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:32.336 # of the computation is cached by cruncher
2025-07-02 05:53:32.344 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:32.352 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:32.359 cruncher.ratio() > best_ratio:
2025-07-02 05:53:32.367 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:32.373 if best_ratio < cutoff:
2025-07-02 05:53:32.378 # no non-identical "pretty close" pair
2025-07-02 05:53:32.385 if eqi is None:
2025-07-02 05:53:32.394 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:32.407 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:32.417 return
2025-07-02 05:53:32.427 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:32.436 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:32.447 else:
2025-07-02 05:53:32.457 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:32.464 eqi = None
2025-07-02 05:53:32.471
2025-07-02 05:53:32.478 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:32.485 # identical
2025-07-02 05:53:32.491
2025-07-02 05:53:32.497 # pump out diffs from before the synch point
2025-07-02 05:53:32.503 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:32.512
2025-07-02 05:53:32.523 # do intraline marking on the synch pair
2025-07-02 05:53:32.531 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:32.542 if eqi is None:
2025-07-02 05:53:32.551 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:32.559 atags = btags = ""
2025-07-02 05:53:32.566 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:32.572 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:32.578 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:32.586 if tag == 'replace':
2025-07-02 05:53:32.598 atags += '^' * la
2025-07-02 05:53:32.607 btags += '^' * lb
2025-07-02 05:53:32.616 elif tag == 'delete':
2025-07-02 05:53:32.624 atags += '-' * la
2025-07-02 05:53:32.631 elif tag == 'insert':
2025-07-02 05:53:32.638 btags += '+' * lb
2025-07-02 05:53:32.645 elif tag == 'equal':
2025-07-02 05:53:32.652 atags += ' ' * la
2025-07-02 05:53:32.659 btags += ' ' * lb
2025-07-02 05:53:32.666 else:
2025-07-02 05:53:32.676 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:32.688 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:32.701 else:
2025-07-02 05:53:32.713 # the synch pair is identical
2025-07-02 05:53:32.723 yield '  ' + aelt
2025-07-02 05:53:32.736
2025-07-02 05:53:32.744 # pump out diffs from after the synch point
2025-07-02 05:53:32.750 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:32.756
2025-07-02 05:53:32.762 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:32.768 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:32.777
2025-07-02 05:53:32.790 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:32.803 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:32.812 alo = 512, ahi = 1101
2025-07-02 05:53:32.823 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:32.834 blo = 512, bhi = 1101
2025-07-02 05:53:32.843
2025-07-02 05:53:32.851 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:32.859 g = []
2025-07-02 05:53:32.873 if alo < ahi:
2025-07-02 05:53:32.883 if blo < bhi:
2025-07-02 05:53:32.891 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:32.898 else:
2025-07-02 05:53:32.907 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:32.915 elif blo < bhi:
2025-07-02 05:53:32.922 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:32.931
2025-07-02 05:53:32.938 >       yield from g
2025-07-02 05:53:32.944
2025-07-02 05:53:32.951 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:32.958 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:32.963
2025-07-02 05:53:32.971 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:32.983 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:32.995 alo = 512, ahi = 1101
2025-07-02 05:53:33.007 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:33.022 blo = 512, bhi = 1101
2025-07-02 05:53:33.031
2025-07-02 05:53:33.042 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:33.054 r"""
2025-07-02 05:53:33.067 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:33.080 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:33.094 synch point, and intraline difference marking is done on the
2025-07-02 05:53:33.106 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:33.116
2025-07-02 05:53:33.123 Example:
2025-07-02 05:53:33.130
2025-07-02 05:53:33.139 >>> d = Differ()
2025-07-02 05:53:33.147 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:33.154 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:33.163 >>> print(''.join(results), end="")
2025-07-02 05:53:33.174 - abcDefghiJkl
2025-07-02 05:53:33.188 + abcdefGhijkl
2025-07-02 05:53:33.198 """
2025-07-02 05:53:33.204
2025-07-02 05:53:33.214 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:33.224 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:33.232 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:33.240 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:33.247 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:33.253
2025-07-02 05:53:33.259 # search for the pair that matches best without being identical
2025-07-02 05:53:33.267 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:33.278 # on junk -- unless we have to)
2025-07-02 05:53:33.292 for j in range(blo, bhi):
2025-07-02 05:53:33.305 bj = b[j]
2025-07-02 05:53:33.314 cruncher.set_seq2(bj)
2025-07-02 05:53:33.323 for i in range(alo, ahi):
2025-07-02 05:53:33.337 ai = a[i]
2025-07-02 05:53:33.346 if ai == bj:
2025-07-02 05:53:33.354 if eqi is None:
2025-07-02 05:53:33.363 eqi, eqj = i, j
2025-07-02 05:53:33.374 continue
2025-07-02 05:53:33.383 cruncher.set_seq1(ai)
2025-07-02 05:53:33.392 # computing similarity is expensive, so use the quick
2025-07-02 05:53:33.399 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:33.409 # compares by a factor of 3.
2025-07-02 05:53:33.421 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:33.432 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:33.441 # of the computation is cached by cruncher
2025-07-02 05:53:33.448 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:33.454 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:33.464 cruncher.ratio() > best_ratio:
2025-07-02 05:53:33.476 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:33.488 if best_ratio < cutoff:
2025-07-02 05:53:33.497 # no non-identical "pretty close" pair
2025-07-02 05:53:33.504 if eqi is None:
2025-07-02 05:53:33.512 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:33.519 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:33.527 return
2025-07-02 05:53:33.537 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:33.549 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:33.561 else:
2025-07-02 05:53:33.573 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:33.586 eqi = None
2025-07-02 05:53:33.594
2025-07-02 05:53:33.603 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:33.610 # identical
2025-07-02 05:53:33.621
2025-07-02 05:53:33.632 # pump out diffs from before the synch point
2025-07-02 05:53:33.640 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:33.648
2025-07-02 05:53:33.654 # do intraline marking on the synch pair
2025-07-02 05:53:33.661 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:33.666 if eqi is None:
2025-07-02 05:53:33.672 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:33.685 atags = btags = ""
2025-07-02 05:53:33.693 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:33.699 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:33.706 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:33.712 if tag == 'replace':
2025-07-02 05:53:33.718 atags += '^' * la
2025-07-02 05:53:33.729 btags += '^' * lb
2025-07-02 05:53:33.738 elif tag == 'delete':
2025-07-02 05:53:33.748 atags += '-' * la
2025-07-02 05:53:33.755 elif tag == 'insert':
2025-07-02 05:53:33.762 btags += '+' * lb
2025-07-02 05:53:33.768 elif tag == 'equal':
2025-07-02 05:53:33.775 atags += ' ' * la
2025-07-02 05:53:33.786 btags += ' ' * lb
2025-07-02 05:53:33.795 else:
2025-07-02 05:53:33.803 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:33.815 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:33.824 else:
2025-07-02 05:53:33.836 # the synch pair is identical
2025-07-02 05:53:33.848 yield '  ' + aelt
2025-07-02 05:53:33.859
2025-07-02 05:53:33.872 # pump out diffs from after the synch point
2025-07-02 05:53:33.881 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:33.887
2025-07-02 05:53:33.894 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:33.907 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:33.918
2025-07-02 05:53:33.928 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:33.937 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:33.943 alo = 513, ahi = 1101
2025-07-02 05:53:33.951 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:33.963 blo = 513, bhi = 1101
2025-07-02 05:53:33.978
2025-07-02 05:53:33.990 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:34.003 g = []
2025-07-02 05:53:34.013 if alo < ahi:
2025-07-02 05:53:34.025 if blo < bhi:
2025-07-02 05:53:34.036 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:34.047 else:
2025-07-02 05:53:34.059 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:34.068 elif blo < bhi:
2025-07-02 05:53:34.076 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:34.087
2025-07-02 05:53:34.100 >       yield from g
2025-07-02 05:53:34.111
2025-07-02 05:53:34.123 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:34.133 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:34.140
2025-07-02 05:53:34.150 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:34.160 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:34.168 alo = 513, ahi = 1101
2025-07-02 05:53:34.176 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:34.181 blo = 513, bhi = 1101
2025-07-02 05:53:34.186
2025-07-02 05:53:34.192 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:34.198 r"""
2025-07-02 05:53:34.210 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:34.220 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:34.227 synch point, and intraline difference marking is done on the
2025-07-02 05:53:34.234 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:34.245
2025-07-02 05:53:34.254 Example:
2025-07-02 05:53:34.261
2025-07-02 05:53:34.267 >>> d = Differ()
2025-07-02 05:53:34.275 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:34.286 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:34.295 >>> print(''.join(results), end="")
2025-07-02 05:53:34.301 - abcDefghiJkl
2025-07-02 05:53:34.325 + abcdefGhijkl
2025-07-02 05:53:34.341 """
2025-07-02 05:53:34.348
2025-07-02 05:53:34.355 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:34.367 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:34.379 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:34.391 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:34.400 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:34.414
2025-07-02 05:53:34.425 # search for the pair that matches best without being identical
2025-07-02 05:53:34.434 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:34.448 # on junk -- unless we have to)
2025-07-02 05:53:34.460 for j in range(blo, bhi):
2025-07-02 05:53:34.472 bj = b[j]
2025-07-02 05:53:34.481 cruncher.set_seq2(bj)
2025-07-02 05:53:34.490 for i in range(alo, ahi):
2025-07-02 05:53:34.501 ai = a[i]
2025-07-02 05:53:34.509 if ai == bj:
2025-07-02 05:53:34.519 if eqi is None:
2025-07-02 05:53:34.532 eqi, eqj = i, j
2025-07-02 05:53:34.542 continue
2025-07-02 05:53:34.554 cruncher.set_seq1(ai)
2025-07-02 05:53:34.565 # computing similarity is expensive, so use the quick
2025-07-02 05:53:34.577 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:34.589 # compares by a factor of 3.
2025-07-02 05:53:34.600 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:34.609 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:34.616 # of the computation is cached by cruncher
2025-07-02 05:53:34.623 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:34.631 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:34.639 cruncher.ratio() > best_ratio:
2025-07-02 05:53:34.647 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:34.653 if best_ratio < cutoff:
2025-07-02 05:53:34.659 # no non-identical "pretty close" pair
2025-07-02 05:53:34.667 if eqi is None:
2025-07-02 05:53:34.678 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:34.687 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:34.694 return
2025-07-02 05:53:34.710 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:34.721 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:34.729 else:
2025-07-02 05:53:34.737 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:34.743 eqi = None
2025-07-02 05:53:34.748
2025-07-02 05:53:34.753 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:34.757 # identical
2025-07-02 05:53:34.765
2025-07-02 05:53:34.772 # pump out diffs from before the synch point
2025-07-02 05:53:34.779 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:34.788
2025-07-02 05:53:34.797 # do intraline marking on the synch pair
2025-07-02 05:53:34.803 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:34.810 if eqi is None:
2025-07-02 05:53:34.820 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:34.829 atags = btags = ""
2025-07-02 05:53:34.837 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:34.843 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:34.852 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:34.863 if tag == 'replace':
2025-07-02 05:53:34.871 atags += '^' * la
2025-07-02 05:53:34.883 btags += '^' * lb
2025-07-02 05:53:34.894 elif tag == 'delete':
2025-07-02 05:53:34.905 atags += '-' * la
2025-07-02 05:53:34.916 elif tag == 'insert':
2025-07-02 05:53:34.925 btags += '+' * lb
2025-07-02 05:53:34.933 elif tag == 'equal':
2025-07-02 05:53:34.940 atags += ' ' * la
2025-07-02 05:53:34.946 btags += ' ' * lb
2025-07-02 05:53:34.953 else:
2025-07-02 05:53:34.965 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:34.973 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:34.981 else:
2025-07-02 05:53:34.988 # the synch pair is identical
2025-07-02 05:53:34.995 yield '  ' + aelt
2025-07-02 05:53:35.004
2025-07-02 05:53:35.016 # pump out diffs from after the synch point
2025-07-02 05:53:35.027 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:35.039
2025-07-02 05:53:35.048 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:35.059 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:35.071
2025-07-02 05:53:35.080 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:35.093 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:35.103 alo = 514, ahi = 1101
2025-07-02 05:53:35.112 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:35.121 blo = 514, bhi = 1101
2025-07-02 05:53:35.129
2025-07-02 05:53:35.137 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:35.143 g = []
2025-07-02 05:53:35.149 if alo < ahi:
2025-07-02 05:53:35.155 if blo < bhi:
2025-07-02 05:53:35.161 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:35.167 else:
2025-07-02 05:53:35.175 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:35.181 elif blo < bhi:
2025-07-02 05:53:35.186 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:35.193
2025-07-02 05:53:35.199 >       yield from g
2025-07-02 05:53:35.205
2025-07-02 05:53:35.215 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:35.228 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:35.238
2025-07-02 05:53:35.247 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:35.256 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:35.262 alo = 514, ahi = 1101
2025-07-02 05:53:35.271 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:35.281 blo = 514, bhi = 1101
2025-07-02 05:53:35.290
2025-07-02 05:53:35.297 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:35.303 r"""
2025-07-02 05:53:35.310 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:35.316 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:35.322 synch point, and intraline difference marking is done on the
2025-07-02 05:53:35.328 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:35.335
2025-07-02 05:53:35.346 Example:
2025-07-02 05:53:35.354
2025-07-02 05:53:35.362 >>> d = Differ()
2025-07-02 05:53:35.370 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:35.379 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:35.386 >>> print(''.join(results), end="")
2025-07-02 05:53:35.395 - abcDefghiJkl
2025-07-02 05:53:35.411 + abcdefGhijkl
2025-07-02 05:53:35.428 """
2025-07-02 05:53:35.434
2025-07-02 05:53:35.440 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:35.446 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:35.457 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:35.468 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:35.479 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:35.489
2025-07-02 05:53:35.498 # search for the pair that matches best without being identical
2025-07-02 05:53:35.506 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:35.517 # on junk -- unless we have to)
2025-07-02 05:53:35.526 for j in range(blo, bhi):
2025-07-02 05:53:35.534 bj = b[j]
2025-07-02 05:53:35.541 cruncher.set_seq2(bj)
2025-07-02 05:53:35.547 for i in range(alo, ahi):
2025-07-02 05:53:35.553 ai = a[i]
2025-07-02 05:53:35.559 if ai == bj:
2025-07-02 05:53:35.567 if eqi is None:
2025-07-02 05:53:35.579 eqi, eqj = i, j
2025-07-02 05:53:35.592 continue
2025-07-02 05:53:35.603 cruncher.set_seq1(ai)
2025-07-02 05:53:35.615 # computing similarity is expensive, so use the quick
2025-07-02 05:53:35.626 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:35.635 # compares by a factor of 3.
2025-07-02 05:53:35.643 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:35.650 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:35.663 # of the computation is cached by cruncher
2025-07-02 05:53:35.671 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:35.678 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:35.688 cruncher.ratio() > best_ratio:
2025-07-02 05:53:35.699 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:35.712 if best_ratio < cutoff:
2025-07-02 05:53:35.723 # no non-identical "pretty close" pair
2025-07-02 05:53:35.731 if eqi is None:
2025-07-02 05:53:35.739 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:35.747 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:35.753 return
2025-07-02 05:53:35.760 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:35.765 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:35.771 else:
2025-07-02 05:53:35.777 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:35.783 eqi = None
2025-07-02 05:53:35.797
2025-07-02 05:53:35.808 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:35.819 # identical
2025-07-02 05:53:35.828
2025-07-02 05:53:35.840 # pump out diffs from before the synch point
2025-07-02 05:53:35.852 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:35.865
2025-07-02 05:53:35.876 # do intraline marking on the synch pair
2025-07-02 05:53:35.889 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:35.900 if eqi is None:
2025-07-02 05:53:35.915 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:35.927 atags = btags = ""
2025-07-02 05:53:35.936 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:35.943 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:35.950 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:35.959 if tag == 'replace':
2025-07-02 05:53:35.970 atags += '^' * la
2025-07-02 05:53:35.978 btags += '^' * lb
2025-07-02 05:53:35.987 elif tag == 'delete':
2025-07-02 05:53:36.000 atags += '-' * la
2025-07-02 05:53:36.015 elif tag == 'insert':
2025-07-02 05:53:36.027 btags += '+' * lb
2025-07-02 05:53:36.035 elif tag == 'equal':
2025-07-02 05:53:36.043 atags += ' ' * la
2025-07-02 05:53:36.051 btags += ' ' * lb
2025-07-02 05:53:36.059 else:
2025-07-02 05:53:36.066 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:36.077 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:36.086 else:
2025-07-02 05:53:36.092 # the synch pair is identical
2025-07-02 05:53:36.098 yield '  ' + aelt
2025-07-02 05:53:36.105
2025-07-02 05:53:36.111 # pump out diffs from after the synch point
2025-07-02 05:53:36.118 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:36.125
2025-07-02 05:53:36.131 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:36.137 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:36.144
2025-07-02 05:53:36.150 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:36.156 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:36.162 alo = 515, ahi = 1101
2025-07-02 05:53:36.168 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:36.173 blo = 515, bhi = 1101
2025-07-02 05:53:36.177
2025-07-02 05:53:36.183 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:36.188 g = []
2025-07-02 05:53:36.193 if alo < ahi:
2025-07-02 05:53:36.199 if blo < bhi:
2025-07-02 05:53:36.204 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:36.209 else:
2025-07-02 05:53:36.214 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:36.219 elif blo < bhi:
2025-07-02 05:53:36.224 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:36.230
2025-07-02 05:53:36.235 >       yield from g
2025-07-02 05:53:36.240
2025-07-02 05:53:36.246 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:36.251 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:36.256
2025-07-02 05:53:36.261 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:36.267 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:36.273 alo = 515, ahi = 1101
2025-07-02 05:53:36.280 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:36.286 blo = 515, bhi = 1101
2025-07-02 05:53:36.292
2025-07-02 05:53:36.298 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:36.305 r"""
2025-07-02 05:53:36.313 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:36.320 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:36.328 synch point, and intraline difference marking is done on the
2025-07-02 05:53:36.336 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:36.343
2025-07-02 05:53:36.350 Example:
2025-07-02 05:53:36.357
2025-07-02 05:53:36.364 >>> d = Differ()
2025-07-02 05:53:36.372 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:36.380 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:36.387 >>> print(''.join(results), end="")
2025-07-02 05:53:36.394 - abcDefghiJkl
2025-07-02 05:53:36.412 + abcdefGhijkl
2025-07-02 05:53:36.431 """
2025-07-02 05:53:36.438
2025-07-02 05:53:36.445 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:36.452 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:36.459 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:36.467 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:36.475 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:36.482
2025-07-02 05:53:36.492 # search for the pair that matches best without being identical
2025-07-02 05:53:36.503 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:36.512 # on junk -- unless we have to)
2025-07-02 05:53:36.519 for j in range(blo, bhi):
2025-07-02 05:53:36.525 bj = b[j]
2025-07-02 05:53:36.530 cruncher.set_seq2(bj)
2025-07-02 05:53:36.536 for i in range(alo, ahi):
2025-07-02 05:53:36.542 ai = a[i]
2025-07-02 05:53:36.548 if ai == bj:
2025-07-02 05:53:36.555 if eqi is None:
2025-07-02 05:53:36.562 eqi, eqj = i, j
2025-07-02 05:53:36.572 continue
2025-07-02 05:53:36.581 cruncher.set_seq1(ai)
2025-07-02 05:53:36.588 # computing similarity is expensive, so use the quick
2025-07-02 05:53:36.594 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:36.599 # compares by a factor of 3.
2025-07-02 05:53:36.604 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:36.609 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:36.615 # of the computation is cached by cruncher
2025-07-02 05:53:36.619 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:36.631 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:36.639 cruncher.ratio() > best_ratio:
2025-07-02 05:53:36.646 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:36.652 if best_ratio < cutoff:
2025-07-02 05:53:36.659 # no non-identical "pretty close" pair
2025-07-02 05:53:36.668 if eqi is None:
2025-07-02 05:53:36.677 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:36.684 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:36.688 return
2025-07-02 05:53:36.694 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:36.700 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:36.705 else:
2025-07-02 05:53:36.710 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:36.716 eqi = None
2025-07-02 05:53:36.722
2025-07-02 05:53:36.734 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:36.741 # identical
2025-07-02 05:53:36.748
2025-07-02 05:53:36.756 # pump out diffs from before the synch point
2025-07-02 05:53:36.769 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:36.779
2025-07-02 05:53:36.788 # do intraline marking on the synch pair
2025-07-02 05:53:36.796 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:36.802 if eqi is None:
2025-07-02 05:53:36.810 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:36.818 atags = btags = ""
2025-07-02 05:53:36.825 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:36.830 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:36.837 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:36.843 if tag == 'replace':
2025-07-02 05:53:36.849 atags += '^' * la
2025-07-02 05:53:36.856 btags += '^' * lb
2025-07-02 05:53:36.863 elif tag == 'delete':
2025-07-02 05:53:36.870 atags += '-' * la
2025-07-02 05:53:36.879 elif tag == 'insert':
2025-07-02 05:53:36.890 btags += '+' * lb
2025-07-02 05:53:36.898 elif tag == 'equal':
2025-07-02 05:53:36.909 atags += ' ' * la
2025-07-02 05:53:36.917 btags += ' ' * lb
2025-07-02 05:53:36.924 else:
2025-07-02 05:53:36.932 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:36.939 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:36.947 else:
2025-07-02 05:53:36.955 # the synch pair is identical
2025-07-02 05:53:36.963 yield '  ' + aelt
2025-07-02 05:53:36.976
2025-07-02 05:53:36.984 # pump out diffs from after the synch point
2025-07-02 05:53:36.991 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:36.997
2025-07-02 05:53:37.004 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:37.010 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:37.017
2025-07-02 05:53:37.028 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:37.038 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:37.045 alo = 516, ahi = 1101
2025-07-02 05:53:37.053 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:37.059 blo = 516, bhi = 1101
2025-07-02 05:53:37.065
2025-07-02 05:53:37.071 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:37.077 g = []
2025-07-02 05:53:37.083 if alo < ahi:
2025-07-02 05:53:37.089 if blo < bhi:
2025-07-02 05:53:37.100 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:37.111 else:
2025-07-02 05:53:37.119 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:37.132 elif blo < bhi:
2025-07-02 05:53:37.141 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:37.148
2025-07-02 05:53:37.156 >       yield from g
2025-07-02 05:53:37.164
2025-07-02 05:53:37.171 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:37.179 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:37.186
2025-07-02 05:53:37.195 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:37.203 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:37.209 alo = 516, ahi = 1101
2025-07-02 05:53:37.218 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:37.225 blo = 516, bhi = 1101
2025-07-02 05:53:37.232
2025-07-02 05:53:37.240 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:37.247 r"""
2025-07-02 05:53:37.255 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:37.262 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:37.272 synch point, and intraline difference marking is done on the
2025-07-02 05:53:37.285 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:37.296
2025-07-02 05:53:37.308 Example:
2025-07-02 05:53:37.321
2025-07-02 05:53:37.329 >>> d = Differ()
2025-07-02 05:53:37.336 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:37.342 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:37.349 >>> print(''.join(results), end="")
2025-07-02 05:53:37.356 - abcDefghiJkl
2025-07-02 05:53:37.369 + abcdefGhijkl
2025-07-02 05:53:37.383 """
2025-07-02 05:53:37.389
2025-07-02 05:53:37.395 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:37.402 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:37.408 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:37.415 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:37.421 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:37.428
2025-07-02 05:53:37.435 # search for the pair that matches best without being identical
2025-07-02 05:53:37.443 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:37.451 # on junk -- unless we have to)
2025-07-02 05:53:37.457 for j in range(blo, bhi):
2025-07-02 05:53:37.463 bj = b[j]
2025-07-02 05:53:37.468 cruncher.set_seq2(bj)
2025-07-02 05:53:37.474 for i in range(alo, ahi):
2025-07-02 05:53:37.479 ai = a[i]
2025-07-02 05:53:37.487 if ai == bj:
2025-07-02 05:53:37.499 if eqi is None:
2025-07-02 05:53:37.512 eqi, eqj = i, j
2025-07-02 05:53:37.522 continue
2025-07-02 05:53:37.532 cruncher.set_seq1(ai)
2025-07-02 05:53:37.545 # computing similarity is expensive, so use the quick
2025-07-02 05:53:37.554 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:37.562 # compares by a factor of 3.
2025-07-02 05:53:37.569 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:37.576 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:37.586 # of the computation is cached by cruncher
2025-07-02 05:53:37.599 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:37.607 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:37.615 cruncher.ratio() > best_ratio:
2025-07-02 05:53:37.625 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:37.634 if best_ratio < cutoff:
2025-07-02 05:53:37.640 # no non-identical "pretty close" pair
2025-07-02 05:53:37.646 if eqi is None:
2025-07-02 05:53:37.657 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:37.667 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:37.685 return
2025-07-02 05:53:37.696 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:37.703 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:37.710 else:
2025-07-02 05:53:37.716 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:37.721 eqi = None
2025-07-02 05:53:37.726
2025-07-02 05:53:37.733 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:37.739 # identical
2025-07-02 05:53:37.747
2025-07-02 05:53:37.759 # pump out diffs from before the synch point
2025-07-02 05:53:37.770 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:37.780
2025-07-02 05:53:37.793 # do intraline marking on the synch pair
2025-07-02 05:53:37.803 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:37.811 if eqi is None:
2025-07-02 05:53:37.825 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:37.837 atags = btags = ""
2025-07-02 05:53:37.851 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:37.860 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:37.868 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:37.874 if tag == 'replace':
2025-07-02 05:53:37.886 atags += '^' * la
2025-07-02 05:53:37.896 btags += '^' * lb
2025-07-02 05:53:37.904 elif tag == 'delete':
2025-07-02 05:53:37.912 atags += '-' * la
2025-07-02 05:53:37.918 elif tag == 'insert':
2025-07-02 05:53:37.925 btags += '+' * lb
2025-07-02 05:53:37.931 elif tag == 'equal':
2025-07-02 05:53:37.939 atags += ' ' * la
2025-07-02 05:53:37.951 btags += ' ' * lb
2025-07-02 05:53:37.961 else:
2025-07-02 05:53:37.969 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:37.976 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:37.982 else:
2025-07-02 05:53:37.993 # the synch pair is identical
2025-07-02 05:53:38.004 yield '  ' + aelt
2025-07-02 05:53:38.012
2025-07-02 05:53:38.019 # pump out diffs from after the synch point
2025-07-02 05:53:38.025 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:38.030
2025-07-02 05:53:38.037 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:38.047 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:38.055
2025-07-02 05:53:38.068 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:38.080 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:38.091 alo = 517, ahi = 1101
2025-07-02 05:53:38.103 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:38.116 blo = 517, bhi = 1101
2025-07-02 05:53:38.127
2025-07-02 05:53:38.140 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:38.150 g = []
2025-07-02 05:53:38.159 if alo < ahi:
2025-07-02 05:53:38.168 if blo < bhi:
2025-07-02 05:53:38.175 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:38.181 else:
2025-07-02 05:53:38.187 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:38.193 elif blo < bhi:
2025-07-02 05:53:38.198 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:38.204
2025-07-02 05:53:38.219 >       yield from g
2025-07-02 05:53:38.230
2025-07-02 05:53:38.243 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:38.255 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:38.264
2025-07-02 05:53:38.272 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:38.283 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:38.292 alo = 517, ahi = 1101
2025-07-02 05:53:38.300 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:38.306 blo = 517, bhi = 1101
2025-07-02 05:53:38.315
2025-07-02 05:53:38.325 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:38.336 r"""
2025-07-02 05:53:38.347 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:38.355 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:38.367 synch point, and intraline difference marking is done on the
2025-07-02 05:53:38.377 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:38.383
2025-07-02 05:53:38.389 Example:
2025-07-02 05:53:38.395
2025-07-02 05:53:38.407 >>> d = Differ()
2025-07-02 05:53:38.420 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:38.433 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:38.444 >>> print(''.join(results), end="")
2025-07-02 05:53:38.456 - abcDefghiJkl
2025-07-02 05:53:38.477 + abcdefGhijkl
2025-07-02 05:53:38.498 """
2025-07-02 05:53:38.507
2025-07-02 05:53:38.515 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:38.528 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:38.536 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:38.542 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:38.549 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:38.557
2025-07-02 05:53:38.564 # search for the pair that matches best without being identical
2025-07-02 05:53:38.570 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:38.575 # on junk -- unless we have to)
2025-07-02 05:53:38.581 for j in range(blo, bhi):
2025-07-02 05:53:38.587 bj = b[j]
2025-07-02 05:53:38.592 cruncher.set_seq2(bj)
2025-07-02 05:53:38.598 for i in range(alo, ahi):
2025-07-02 05:53:38.608 ai = a[i]
2025-07-02 05:53:38.619 if ai == bj:
2025-07-02 05:53:38.627 if eqi is None:
2025-07-02 05:53:38.634 eqi, eqj = i, j
2025-07-02 05:53:38.641 continue
2025-07-02 05:53:38.647 cruncher.set_seq1(ai)
2025-07-02 05:53:38.655 # computing similarity is expensive, so use the quick
2025-07-02 05:53:38.667 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:38.677 # compares by a factor of 3.
2025-07-02 05:53:38.685 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:38.692 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:38.699 # of the computation is cached by cruncher
2025-07-02 05:53:38.705 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:38.710 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:38.720 cruncher.ratio() > best_ratio:
2025-07-02 05:53:38.731 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:38.739 if best_ratio < cutoff:
2025-07-02 05:53:38.752 # no non-identical "pretty close" pair
2025-07-02 05:53:38.764 if eqi is None:
2025-07-02 05:53:38.774 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:38.783 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:38.791 return
2025-07-02 05:53:38.799 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:38.810 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:38.823 else:
2025-07-02 05:53:38.831 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:38.838 eqi = None
2025-07-02 05:53:38.845
2025-07-02 05:53:38.853 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:38.860 # identical
2025-07-02 05:53:38.867
2025-07-02 05:53:38.875 # pump out diffs from before the synch point
2025-07-02 05:53:38.883 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:38.890
2025-07-02 05:53:38.902 # do intraline marking on the synch pair
2025-07-02 05:53:38.911 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:38.918 if eqi is None:
2025-07-02 05:53:38.925 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:38.931 atags = btags = ""
2025-07-02 05:53:38.937 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:38.944 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:38.951 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:38.958 if tag == 'replace':
2025-07-02 05:53:38.969 atags += '^' * la
2025-07-02 05:53:38.978 btags += '^' * lb
2025-07-02 05:53:38.986 elif tag == 'delete':
2025-07-02 05:53:38.993 atags += '-' * la
2025-07-02 05:53:39.000 elif tag == 'insert':
2025-07-02 05:53:39.007 btags += '+' * lb
2025-07-02 05:53:39.015 elif tag == 'equal':
2025-07-02 05:53:39.022 atags += ' ' * la
2025-07-02 05:53:39.033 btags += ' ' * lb
2025-07-02 05:53:39.044 else:
2025-07-02 05:53:39.053 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:39.060 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:39.071 else:
2025-07-02 05:53:39.079 # the synch pair is identical
2025-07-02 05:53:39.085 yield '  ' + aelt
2025-07-02 05:53:39.091
2025-07-02 05:53:39.096 # pump out diffs from after the synch point
2025-07-02 05:53:39.100 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:39.105
2025-07-02 05:53:39.112 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:39.119 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:39.126
2025-07-02 05:53:39.132 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:39.140 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:39.146 alo = 518, ahi = 1101
2025-07-02 05:53:39.155 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:39.162 blo = 518, bhi = 1101
2025-07-02 05:53:39.171
2025-07-02 05:53:39.183 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:39.192 g = []
2025-07-02 05:53:39.198 if alo < ahi:
2025-07-02 05:53:39.204 if blo < bhi:
2025-07-02 05:53:39.211 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:39.217 else:
2025-07-02 05:53:39.228 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:39.238 elif blo < bhi:
2025-07-02 05:53:39.245 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:39.252
2025-07-02 05:53:39.258 >       yield from g
2025-07-02 05:53:39.264
2025-07-02 05:53:39.271 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:39.279 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:39.287
2025-07-02 05:53:39.298 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:39.306 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:39.316 alo = 518, ahi = 1101
2025-07-02 05:53:39.328 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:39.339 blo = 518, bhi = 1101
2025-07-02 05:53:39.350
2025-07-02 05:53:39.359 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:39.367 r"""
2025-07-02 05:53:39.377 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:39.387 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:39.398 synch point, and intraline difference marking is done on the
2025-07-02 05:53:39.409 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:39.419
2025-07-02 05:53:39.428 Example:
2025-07-02 05:53:39.435
2025-07-02 05:53:39.441 >>> d = Differ()
2025-07-02 05:53:39.447 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:39.453 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:39.459 >>> print(''.join(results), end="")
2025-07-02 05:53:39.465 - abcDefghiJkl
2025-07-02 05:53:39.487 + abcdefGhijkl
2025-07-02 05:53:39.503 """
2025-07-02 05:53:39.511
2025-07-02 05:53:39.518 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:39.529 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:39.540 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:39.548 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:39.555 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:39.563
2025-07-02 05:53:39.575 # search for the pair that matches best without being identical
2025-07-02 05:53:39.585 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:39.593 # on junk -- unless we have to)
2025-07-02 05:53:39.601 for j in range(blo, bhi):
2025-07-02 05:53:39.608 bj = b[j]
2025-07-02 05:53:39.617 cruncher.set_seq2(bj)
2025-07-02 05:53:39.627 for i in range(alo, ahi):
2025-07-02 05:53:39.639 ai = a[i]
2025-07-02 05:53:39.649 if ai == bj:
2025-07-02 05:53:39.661 if eqi is None:
2025-07-02 05:53:39.667 eqi, eqj = i, j
2025-07-02 05:53:39.672 continue
2025-07-02 05:53:39.678 cruncher.set_seq1(ai)
2025-07-02 05:53:39.687 # computing similarity is expensive, so use the quick
2025-07-02 05:53:39.699 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:39.709 # compares by a factor of 3.
2025-07-02 05:53:39.717 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:39.724 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:39.730 # of the computation is cached by cruncher
2025-07-02 05:53:39.738 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:39.745 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:39.752 cruncher.ratio() > best_ratio:
2025-07-02 05:53:39.766 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:39.777 if best_ratio < cutoff:
2025-07-02 05:53:39.786 # no non-identical "pretty close" pair
2025-07-02 05:53:39.793 if eqi is None:
2025-07-02 05:53:39.799 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:39.807 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:39.815 return
2025-07-02 05:53:39.827 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:39.836 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:39.843 else:
2025-07-02 05:53:39.850 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:39.864 eqi = None
2025-07-02 05:53:39.878
2025-07-02 05:53:39.889 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:39.902 # identical
2025-07-02 05:53:39.912
2025-07-02 05:53:39.919 # pump out diffs from before the synch point
2025-07-02 05:53:39.926 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:39.935
2025-07-02 05:53:39.942 # do intraline marking on the synch pair
2025-07-02 05:53:39.952 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:39.958 if eqi is None:
2025-07-02 05:53:39.964 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:39.977 atags = btags = ""
2025-07-02 05:53:39.991 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:40.004 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:40.013 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:40.025 if tag == 'replace':
2025-07-02 05:53:40.039 atags += '^' * la
2025-07-02 05:53:40.051 btags += '^' * lb
2025-07-02 05:53:40.060 elif tag == 'delete':
2025-07-02 05:53:40.067 atags += '-' * la
2025-07-02 05:53:40.075 elif tag == 'insert':
2025-07-02 05:53:40.085 btags += '+' * lb
2025-07-02 05:53:40.094 elif tag == 'equal':
2025-07-02 05:53:40.101 atags += ' ' * la
2025-07-02 05:53:40.108 btags += ' ' * lb
2025-07-02 05:53:40.118 else:
2025-07-02 05:53:40.129 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:40.141 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:40.154 else:
2025-07-02 05:53:40.166 # the synch pair is identical
2025-07-02 05:53:40.178 yield '  ' + aelt
2025-07-02 05:53:40.189
2025-07-02 05:53:40.198 # pump out diffs from after the synch point
2025-07-02 05:53:40.207 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:40.214
2025-07-02 05:53:40.220 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:40.227 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:40.234
2025-07-02 05:53:40.247 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:40.259 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:40.267 alo = 519, ahi = 1101
2025-07-02 05:53:40.275 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:40.282 blo = 519, bhi = 1101
2025-07-02 05:53:40.289
2025-07-02 05:53:40.295 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:40.300 g = []
2025-07-02 05:53:40.306 if alo < ahi:
2025-07-02 05:53:40.316 if blo < bhi:
2025-07-02 05:53:40.326 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:40.335 else:
2025-07-02 05:53:40.342 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:40.352 elif blo < bhi:
2025-07-02 05:53:40.361 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:40.369
2025-07-02 05:53:40.376 >       yield from g
2025-07-02 05:53:40.382
2025-07-02 05:53:40.392 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:40.402 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:40.410
2025-07-02 05:53:40.418 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:40.426 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:40.438 alo = 519, ahi = 1101
2025-07-02 05:53:40.450 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:40.462 blo = 519, bhi = 1101
2025-07-02 05:53:40.473
2025-07-02 05:53:40.484 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:40.493 r"""
2025-07-02 05:53:40.501 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:40.508 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:40.514 synch point, and intraline difference marking is done on the
2025-07-02 05:53:40.525 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:40.537
2025-07-02 05:53:40.543 Example:
2025-07-02 05:53:40.551
2025-07-02 05:53:40.563 >>> d = Differ()
2025-07-02 05:53:40.579 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:40.594 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:40.603 >>> print(''.join(results), end="")
2025-07-02 05:53:40.611 - abcDefghiJkl
2025-07-02 05:53:40.624 + abcdefGhijkl
2025-07-02 05:53:40.634 """
2025-07-02 05:53:40.638
2025-07-02 05:53:40.643 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:40.648 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:40.653 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:40.658 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:40.671 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:40.682
2025-07-02 05:53:40.690 # search for the pair that matches best without being identical
2025-07-02 05:53:40.700 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:40.713 # on junk -- unless we have to)
2025-07-02 05:53:40.727 for j in range(blo, bhi):
2025-07-02 05:53:40.739 bj = b[j]
2025-07-02 05:53:40.749 cruncher.set_seq2(bj)
2025-07-02 05:53:40.759 for i in range(alo, ahi):
2025-07-02 05:53:40.771 ai = a[i]
2025-07-02 05:53:40.782 if ai == bj:
2025-07-02 05:53:40.794 if eqi is None:
2025-07-02 05:53:40.806 eqi, eqj = i, j
2025-07-02 05:53:40.819 continue
2025-07-02 05:53:40.830 cruncher.set_seq1(ai)
2025-07-02 05:53:40.839 # computing similarity is expensive, so use the quick
2025-07-02 05:53:40.851 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:40.862 # compares by a factor of 3.
2025-07-02 05:53:40.869 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:40.876 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:40.888 # of the computation is cached by cruncher
2025-07-02 05:53:40.899 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:40.910 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:40.920 cruncher.ratio() > best_ratio:
2025-07-02 05:53:40.928 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:40.935 if best_ratio < cutoff:
2025-07-02 05:53:40.942 # no non-identical "pretty close" pair
2025-07-02 05:53:40.947 if eqi is None:
2025-07-02 05:53:40.955 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:40.966 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:40.974 return
2025-07-02 05:53:40.981 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:40.987 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:40.994 else:
2025-07-02 05:53:41.007 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:41.018 eqi = None
2025-07-02 05:53:41.029
2025-07-02 05:53:41.041 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:41.054 # identical
2025-07-02 05:53:41.068
2025-07-02 05:53:41.079 # pump out diffs from before the synch point
2025-07-02 05:53:41.089 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:41.101
2025-07-02 05:53:41.112 # do intraline marking on the synch pair
2025-07-02 05:53:41.123 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:41.134 if eqi is None:
2025-07-02 05:53:41.143 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:41.150 atags = btags = ""
2025-07-02 05:53:41.158 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:41.166 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:41.172 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:41.178 if tag == 'replace':
2025-07-02 05:53:41.183 atags += '^' * la
2025-07-02 05:53:41.191 btags += '^' * lb
2025-07-02 05:53:41.203 elif tag == 'delete':
2025-07-02 05:53:41.212 atags += '-' * la
2025-07-02 05:53:41.219 elif tag == 'insert':
2025-07-02 05:53:41.225 btags += '+' * lb
2025-07-02 05:53:41.238 elif tag == 'equal':
2025-07-02 05:53:41.250 atags += ' ' * la
2025-07-02 05:53:41.259 btags += ' ' * lb
2025-07-02 05:53:41.269 else:
2025-07-02 05:53:41.280 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:41.293 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:41.302 else:
2025-07-02 05:53:41.313 # the synch pair is identical
2025-07-02 05:53:41.326 yield '  ' + aelt
2025-07-02 05:53:41.339
2025-07-02 05:53:41.347 # pump out diffs from after the synch point
2025-07-02 05:53:41.353 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:41.358
2025-07-02 05:53:41.362 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:41.367 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:41.372
2025-07-02 05:53:41.376 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:41.381 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:41.386 alo = 520, ahi = 1101
2025-07-02 05:53:41.391 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:41.402 blo = 520, bhi = 1101
2025-07-02 05:53:41.411
2025-07-02 05:53:41.419 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:41.428 g = []
2025-07-02 05:53:41.439 if alo < ahi:
2025-07-02 05:53:41.449 if blo < bhi:
2025-07-02 05:53:41.459 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:41.471 else:
2025-07-02 05:53:41.481 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:41.489 elif blo < bhi:
2025-07-02 05:53:41.496 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:41.502
2025-07-02 05:53:41.516 >       yield from g
2025-07-02 05:53:41.528
2025-07-02 05:53:41.541 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:41.555 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:41.565
2025-07-02 05:53:41.579 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:41.592 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:41.606 alo = 520, ahi = 1101
2025-07-02 05:53:41.615 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:41.622 blo = 520, bhi = 1101
2025-07-02 05:53:41.628
2025-07-02 05:53:41.634 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:41.640 r"""
2025-07-02 05:53:41.647 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:41.655 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:41.665 synch point, and intraline difference marking is done on the
2025-07-02 05:53:41.674 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:41.682
2025-07-02 05:53:41.689 Example:
2025-07-02 05:53:41.703
2025-07-02 05:53:41.713 >>> d = Differ()
2025-07-02 05:53:41.720 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:41.730 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:41.737 >>> print(''.join(results), end="")
2025-07-02 05:53:41.745 - abcDefghiJkl
2025-07-02 05:53:41.758 + abcdefGhijkl
2025-07-02 05:53:41.770 """
2025-07-02 05:53:41.783
2025-07-02 05:53:41.793 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:41.801 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:41.808 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:41.814 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:41.821 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:41.827
2025-07-02 05:53:41.835 # search for the pair that matches best without being identical
2025-07-02 05:53:41.847 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:41.857 # on junk -- unless we have to)
2025-07-02 05:53:41.865 for j in range(blo, bhi):
2025-07-02 05:53:41.871 bj = b[j]
2025-07-02 05:53:41.877 cruncher.set_seq2(bj)
2025-07-02 05:53:41.883 for i in range(alo, ahi):
2025-07-02 05:53:41.888 ai = a[i]
2025-07-02 05:53:41.895 if ai == bj:
2025-07-02 05:53:41.907 if eqi is None:
2025-07-02 05:53:41.917 eqi, eqj = i, j
2025-07-02 05:53:41.927 continue
2025-07-02 05:53:41.935 cruncher.set_seq1(ai)
2025-07-02 05:53:41.946 # computing similarity is expensive, so use the quick
2025-07-02 05:53:41.958 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:41.968 # compares by a factor of 3.
2025-07-02 05:53:41.976 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:41.982 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:41.989 # of the computation is cached by cruncher
2025-07-02 05:53:41.995 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:42.004 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:42.011 cruncher.ratio() > best_ratio:
2025-07-02 05:53:42.018 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:42.031 if best_ratio < cutoff:
2025-07-02 05:53:42.043 # no non-identical "pretty close" pair
2025-07-02 05:53:42.054 if eqi is None:
2025-07-02 05:53:42.065 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:42.075 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:42.088 return
2025-07-02 05:53:42.098 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:42.108 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:42.120 else:
2025-07-02 05:53:42.132 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:42.141 eqi = None
2025-07-02 05:53:42.154
2025-07-02 05:53:42.166 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:42.175 # identical
2025-07-02 05:53:42.185
2025-07-02 05:53:42.195 # pump out diffs from before the synch point
2025-07-02 05:53:42.203 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:42.211
2025-07-02 05:53:42.220 # do intraline marking on the synch pair
2025-07-02 05:53:42.230 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:42.239 if eqi is None:
2025-07-02 05:53:42.247 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:42.258 atags = btags = ""
2025-07-02 05:53:42.267 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:42.274 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:42.281 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:42.287 if tag == 'replace':
2025-07-02 05:53:42.295 atags += '^' * la
2025-07-02 05:53:42.302 btags += '^' * lb
2025-07-02 05:53:42.313 elif tag == 'delete':
2025-07-02 05:53:42.323 atags += '-' * la
2025-07-02 05:53:42.331 elif tag == 'insert':
2025-07-02 05:53:42.338 btags += '+' * lb
2025-07-02 05:53:42.350 elif tag == 'equal':
2025-07-02 05:53:42.358 atags += ' ' * la
2025-07-02 05:53:42.365 btags += ' ' * lb
2025-07-02 05:53:42.377 else:
2025-07-02 05:53:42.387 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:42.396 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:42.403 else:
2025-07-02 05:53:42.410 # the synch pair is identical
2025-07-02 05:53:42.424 yield '  ' + aelt
2025-07-02 05:53:42.435
2025-07-02 05:53:42.445 # pump out diffs from after the synch point
2025-07-02 05:53:42.456 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:42.465
2025-07-02 05:53:42.473 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:42.481 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:42.487
2025-07-02 05:53:42.493 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:42.501 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:42.507 alo = 521, ahi = 1101
2025-07-02 05:53:42.514 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:42.521 blo = 521, bhi = 1101
2025-07-02 05:53:42.526
2025-07-02 05:53:42.537 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:42.546 g = []
2025-07-02 05:53:42.554 if alo < ahi:
2025-07-02 05:53:42.561 if blo < bhi:
2025-07-02 05:53:42.567 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:42.572 else:
2025-07-02 05:53:42.578 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:42.584 elif blo < bhi:
2025-07-02 05:53:42.590 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:42.601
2025-07-02 05:53:42.611 >       yield from g
2025-07-02 05:53:42.619
2025-07-02 05:53:42.626 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:42.633 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:42.639
2025-07-02 05:53:42.644 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:42.652 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:42.658 alo = 521, ahi = 1101
2025-07-02 05:53:42.665 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:42.670 blo = 521, bhi = 1101
2025-07-02 05:53:42.681
2025-07-02 05:53:42.690 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:42.699 r"""
2025-07-02 05:53:42.712 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:42.721 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:42.730 synch point, and intraline difference marking is done on the
2025-07-02 05:53:42.743 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:42.753
2025-07-02 05:53:42.761 Example:
2025-07-02 05:53:42.768
2025-07-02 05:53:42.775 >>> d = Differ()
2025-07-02 05:53:42.784 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:42.792 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:42.799 >>> print(''.join(results), end="")
2025-07-02 05:53:42.805 - abcDefghiJkl
2025-07-02 05:53:42.822 + abcdefGhijkl
2025-07-02 05:53:42.841 """
2025-07-02 05:53:42.848
2025-07-02 05:53:42.854 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:42.864 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:42.873 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:42.886 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:42.897 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:42.905
2025-07-02 05:53:42.914 # search for the pair that matches best without being identical
2025-07-02 05:53:42.926 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:42.940 # on junk -- unless we have to)
2025-07-02 05:53:42.953 for j in range(blo, bhi):
2025-07-02 05:53:42.963 bj = b[j]
2025-07-02 05:53:42.971 cruncher.set_seq2(bj)
2025-07-02 05:53:42.979 for i in range(alo, ahi):
2025-07-02 05:53:42.992 ai = a[i]
2025-07-02 05:53:43.004 if ai == bj:
2025-07-02 05:53:43.018 if eqi is None:
2025-07-02 05:53:43.029 eqi, eqj = i, j
2025-07-02 05:53:43.041 continue
2025-07-02 05:53:43.053 cruncher.set_seq1(ai)
2025-07-02 05:53:43.064 # computing similarity is expensive, so use the quick
2025-07-02 05:53:43.075 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:43.084 # compares by a factor of 3.
2025-07-02 05:53:43.091 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:43.099 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:43.107 # of the computation is cached by cruncher
2025-07-02 05:53:43.115 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:43.129 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:43.139 cruncher.ratio() > best_ratio:
2025-07-02 05:53:43.152 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:43.161 if best_ratio < cutoff:
2025-07-02 05:53:43.169 # no non-identical "pretty close" pair
2025-07-02 05:53:43.174 if eqi is None:
2025-07-02 05:53:43.179 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:43.187 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:43.197 return
2025-07-02 05:53:43.206 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:43.214 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:43.224 else:
2025-07-02 05:53:43.236 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:43.244 eqi = None
2025-07-02 05:53:43.251
2025-07-02 05:53:43.259 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:43.270 # identical
2025-07-02 05:53:43.277
2025-07-02 05:53:43.284 # pump out diffs from before the synch point
2025-07-02 05:53:43.291 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:43.299
2025-07-02 05:53:43.311 # do intraline marking on the synch pair
2025-07-02 05:53:43.319 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:43.326 if eqi is None:
2025-07-02 05:53:43.338 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:43.349 atags = btags = ""
2025-07-02 05:53:43.363 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:43.374 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:43.382 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:43.391 if tag == 'replace':
2025-07-02 05:53:43.401 atags += '^' * la
2025-07-02 05:53:43.415 btags += '^' * lb
2025-07-02 05:53:43.425 elif tag == 'delete':
2025-07-02 05:53:43.435 atags += '-' * la
2025-07-02 05:53:43.443 elif tag == 'insert':
2025-07-02 05:53:43.449 btags += '+' * lb
2025-07-02 05:53:43.454 elif tag == 'equal':
2025-07-02 05:53:43.459 atags += ' ' * la
2025-07-02 05:53:43.465 btags += ' ' * lb
2025-07-02 05:53:43.471 else:
2025-07-02 05:53:43.476 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:43.480 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:43.485 else:
2025-07-02 05:53:43.489 # the synch pair is identical
2025-07-02 05:53:43.493 yield '  ' + aelt
2025-07-02 05:53:43.498
2025-07-02 05:53:43.502 # pump out diffs from after the synch point
2025-07-02 05:53:43.507 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:43.511
2025-07-02 05:53:43.515 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:43.520 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:43.524
2025-07-02 05:53:43.529 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:43.534 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:43.538 alo = 522, ahi = 1101
2025-07-02 05:53:43.543 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:43.547 blo = 522, bhi = 1101
2025-07-02 05:53:43.552
2025-07-02 05:53:43.556 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:43.560 g = []
2025-07-02 05:53:43.565 if alo < ahi:
2025-07-02 05:53:43.569 if blo < bhi:
2025-07-02 05:53:43.583 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:43.592 else:
2025-07-02 05:53:43.600 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:43.607 elif blo < bhi:
2025-07-02 05:53:43.614 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:43.621
2025-07-02 05:53:43.627 >       yield from g
2025-07-02 05:53:43.633
2025-07-02 05:53:43.638 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:43.644 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:43.649
2025-07-02 05:53:43.654 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:43.660 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:43.666 alo = 522, ahi = 1101
2025-07-02 05:53:43.674 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:43.685 blo = 522, bhi = 1101
2025-07-02 05:53:43.695
2025-07-02 05:53:43.703 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:43.709 r"""
2025-07-02 05:53:43.721 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:43.730 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:43.739 synch point, and intraline difference marking is done on the
2025-07-02 05:53:43.749 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:43.757
2025-07-02 05:53:43.771 Example:
2025-07-02 05:53:43.784
2025-07-02 05:53:43.794 >>> d = Differ()
2025-07-02 05:53:43.800 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:43.806 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:43.815 >>> print(''.join(results), end="")
2025-07-02 05:53:43.822 - abcDefghiJkl
2025-07-02 05:53:43.834 + abcdefGhijkl
2025-07-02 05:53:43.846 """
2025-07-02 05:53:43.852
2025-07-02 05:53:43.858 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:43.865 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:43.877 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:43.888 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:43.898 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:43.905
2025-07-02 05:53:43.911 # search for the pair that matches best without being identical
2025-07-02 05:53:43.918 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:43.925 # on junk -- unless we have to)
2025-07-02 05:53:43.930 for j in range(blo, bhi):
2025-07-02 05:53:43.936 bj = b[j]
2025-07-02 05:53:43.942 cruncher.set_seq2(bj)
2025-07-02 05:53:43.948 for i in range(alo, ahi):
2025-07-02 05:53:43.955 ai = a[i]
2025-07-02 05:53:43.966 if ai == bj:
2025-07-02 05:53:43.979 if eqi is None:
2025-07-02 05:53:43.990 eqi, eqj = i, j
2025-07-02 05:53:44.002 continue
2025-07-02 05:53:44.015 cruncher.set_seq1(ai)
2025-07-02 05:53:44.026 # computing similarity is expensive, so use the quick
2025-07-02 05:53:44.036 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:44.049 # compares by a factor of 3.
2025-07-02 05:53:44.061 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:44.074 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:44.086 # of the computation is cached by cruncher
2025-07-02 05:53:44.096 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:44.103 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:44.110 cruncher.ratio() > best_ratio:
2025-07-02 05:53:44.122 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:44.135 if best_ratio < cutoff:
2025-07-02 05:53:44.141 # no non-identical "pretty close" pair
2025-07-02 05:53:44.148 if eqi is None:
2025-07-02 05:53:44.155 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:44.164 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:44.174 return
2025-07-02 05:53:44.184 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:44.192 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:44.200 else:
2025-07-02 05:53:44.207 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:44.214 eqi = None
2025-07-02 05:53:44.220
2025-07-02 05:53:44.229 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:44.237 # identical
2025-07-02 05:53:44.248
2025-07-02 05:53:44.260 # pump out diffs from before the synch point
2025-07-02 05:53:44.273 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:44.284
2025-07-02 05:53:44.295 # do intraline marking on the synch pair
2025-07-02 05:53:44.305 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:44.321 if eqi is None:
2025-07-02 05:53:44.330 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:44.337 atags = btags = ""
2025-07-02 05:53:44.344 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:44.350 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:44.356 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:44.362 if tag == 'replace':
2025-07-02 05:53:44.368 atags += '^' * la
2025-07-02 05:53:44.374 btags += '^' * lb
2025-07-02 05:53:44.380 elif tag == 'delete':
2025-07-02 05:53:44.385 atags += '-' * la
2025-07-02 05:53:44.392 elif tag == 'insert':
2025-07-02 05:53:44.398 btags += '+' * lb
2025-07-02 05:53:44.409 elif tag == 'equal':
2025-07-02 05:53:44.418 atags += ' ' * la
2025-07-02 05:53:44.426 btags += ' ' * lb
2025-07-02 05:53:44.432 else:
2025-07-02 05:53:44.438 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:44.444 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:44.450 else:
2025-07-02 05:53:44.456 # the synch pair is identical
2025-07-02 05:53:44.462 yield '  ' + aelt
2025-07-02 05:53:44.470
2025-07-02 05:53:44.483 # pump out diffs from after the synch point
2025-07-02 05:53:44.494 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:44.504
2025-07-02 05:53:44.515 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:44.525 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:44.532
2025-07-02 05:53:44.541 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:44.549 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:44.556 alo = 523, ahi = 1101
2025-07-02 05:53:44.564 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:44.569 blo = 523, bhi = 1101
2025-07-02 05:53:44.574
2025-07-02 05:53:44.580 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:44.585 g = []
2025-07-02 05:53:44.590 if alo < ahi:
2025-07-02 05:53:44.600 if blo < bhi:
2025-07-02 05:53:44.612 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:44.620 else:
2025-07-02 05:53:44.628 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:44.634 elif blo < bhi:
2025-07-02 05:53:44.640 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:44.649
2025-07-02 05:53:44.658 >       yield from g
2025-07-02 05:53:44.665
2025-07-02 05:53:44.672 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:44.679 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:44.687
2025-07-02 05:53:44.698 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:44.708 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:44.717 alo = 523, ahi = 1101
2025-07-02 05:53:44.727 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:44.738 blo = 523, bhi = 1101
2025-07-02 05:53:44.750
2025-07-02 05:53:44.760 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:44.768 r"""
2025-07-02 05:53:44.776 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:44.783 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:44.791 synch point, and intraline difference marking is done on the
2025-07-02 05:53:44.801 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:44.807
2025-07-02 05:53:44.815 Example:
2025-07-02 05:53:44.821
2025-07-02 05:53:44.827 >>> d = Differ()
2025-07-02 05:53:44.831 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:44.836 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:44.840 >>> print(''.join(results), end="")
2025-07-02 05:53:44.845 - abcDefghiJkl
2025-07-02 05:53:44.854 + abcdefGhijkl
2025-07-02 05:53:44.862 """
2025-07-02 05:53:44.867
2025-07-02 05:53:44.871 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:44.876 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:44.880 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:44.885 cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:44.889 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-02 05:53:44.894
2025-07-02 05:53:44.898 # search for the pair that matches best without being identical
2025-07-02 05:53:44.903 # (identical lines must be junk lines, & we don't want to synch up
2025-07-02 05:53:44.907 # on junk -- unless we have to)
2025-07-02 05:53:44.912 for j in range(blo, bhi):
2025-07-02 05:53:44.916 bj = b[j]
2025-07-02 05:53:44.921 cruncher.set_seq2(bj)
2025-07-02 05:53:44.925 for i in range(alo, ahi):
2025-07-02 05:53:44.930 ai = a[i]
2025-07-02 05:53:44.934 if ai == bj:
2025-07-02 05:53:44.938 if eqi is None:
2025-07-02 05:53:44.943 eqi, eqj = i, j
2025-07-02 05:53:44.947 continue
2025-07-02 05:53:44.952 cruncher.set_seq1(ai)
2025-07-02 05:53:44.956 # computing similarity is expensive, so use the quick
2025-07-02 05:53:44.961 # upper bounds first -- have seen this speed up messy
2025-07-02 05:53:44.966 # compares by a factor of 3.
2025-07-02 05:53:44.970 # note that ratio() is only expensive to compute the first
2025-07-02 05:53:44.975 # time it's called on a sequence pair; the expensive part
2025-07-02 05:53:44.979 # of the computation is cached by cruncher
2025-07-02 05:53:44.989 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-02 05:53:45.000 cruncher.quick_ratio() > best_ratio and \
2025-07-02 05:53:45.008 cruncher.ratio() > best_ratio:
2025-07-02 05:53:45.016 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-02 05:53:45.024 if best_ratio < cutoff:
2025-07-02 05:53:45.034 # no non-identical "pretty close" pair
2025-07-02 05:53:45.043 if eqi is None:
2025-07-02 05:53:45.052 # no identical pair either -- treat it as a straight replace
2025-07-02 05:53:45.060 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:45.067 return
2025-07-02 05:53:45.075 # no close pair, but an identical pair -- synch up on that
2025-07-02 05:53:45.085 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-02 05:53:45.094 else:
2025-07-02 05:53:45.101 # there's a close pair, so forget the identical pair (if any)
2025-07-02 05:53:45.106 eqi = None
2025-07-02 05:53:45.112
2025-07-02 05:53:45.118 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-02 05:53:45.124 # identical
2025-07-02 05:53:45.133
2025-07-02 05:53:45.147 # pump out diffs from before the synch point
2025-07-02 05:53:45.159 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-02 05:53:45.168
2025-07-02 05:53:45.175 # do intraline marking on the synch pair
2025-07-02 05:53:45.182 aelt, belt = a[best_i], b[best_j]
2025-07-02 05:53:45.188 if eqi is None:
2025-07-02 05:53:45.194 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-02 05:53:45.203 atags = btags = ""
2025-07-02 05:53:45.218 cruncher.set_seqs(aelt, belt)
2025-07-02 05:53:45.231 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-02 05:53:45.241 la, lb = ai2 - ai1, bj2 - bj1
2025-07-02 05:53:45.249 if tag == 'replace':
2025-07-02 05:53:45.255 atags += '^' * la
2025-07-02 05:53:45.263 btags += '^' * lb
2025-07-02 05:53:45.274 elif tag == 'delete':
2025-07-02 05:53:45.287 atags += '-' * la
2025-07-02 05:53:45.297 elif tag == 'insert':
2025-07-02 05:53:45.307 btags += '+' * lb
2025-07-02 05:53:45.316 elif tag == 'equal':
2025-07-02 05:53:45.328 atags += ' ' * la
2025-07-02 05:53:45.339 btags += ' ' * lb
2025-07-02 05:53:45.349 else:
2025-07-02 05:53:45.361 raise ValueError('unknown tag %r' % (tag,))
2025-07-02 05:53:45.374 yield from self._qformat(aelt, belt, atags, btags)
2025-07-02 05:53:45.387 else:
2025-07-02 05:53:45.400 # the synch pair is identical
2025-07-02 05:53:45.412 yield '  ' + aelt
2025-07-02 05:53:45.422
2025-07-02 05:53:45.432 # pump out diffs from after the synch point
2025-07-02 05:53:45.445 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-02 05:53:45.457
2025-07-02 05:53:45.463 /usr/lib/python3.11/difflib.py:985:
2025-07-02 05:53:45.476 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:45.486
2025-07-02 05:53:45.498 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:45.512 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:45.522 alo = 524, ahi = 1101
2025-07-02 05:53:45.537 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:45.548 blo = 524, bhi = 1101
2025-07-02 05:53:45.555
2025-07-02 05:53:45.562 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:45.569 g = []
2025-07-02 05:53:45.575 if alo < ahi:
2025-07-02 05:53:45.583 if blo < bhi:
2025-07-02 05:53:45.594 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-02 05:53:45.602 else:
2025-07-02 05:53:45.611 g = self._dump('-', a, alo, ahi)
2025-07-02 05:53:45.622 elif blo < bhi:
2025-07-02 05:53:45.633 g = self._dump('+', b, blo, bhi)
2025-07-02 05:53:45.643
2025-07-02 05:53:45.651 >       yield from g
2025-07-02 05:53:45.659
2025-07-02 05:53:45.671 /usr/lib/python3.11/difflib.py:997:
2025-07-02 05:53:45.681 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:45.693
2025-07-02 05:53:45.707 self = <difflib.Differ object at [hex]>
2025-07-02 05:53:45.721 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-02 05:53:45.734 alo = 524, ahi = 1101
2025-07-02 05:53:45.747 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-02 05:53:45.756 blo = 524, bhi = 1101
2025-07-02 05:53:45.763
2025-07-02 05:53:45.773 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-02 05:53:45.784 r"""
2025-07-02 05:53:45.793 When replacing one block of lines with another, search the blocks
2025-07-02 05:53:45.801 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-02 05:53:45.808 synch point, and intraline difference marking is done on the
2025-07-02 05:53:45.814 similar pair. Lots of work, but often worth it.
2025-07-02 05:53:45.819
2025-07-02 05:53:45.825 Example:
2025-07-02 05:53:45.831
2025-07-02 05:53:45.836 >>> d = Differ()
2025-07-02 05:53:45.843 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-02 05:53:45.856 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-02 05:53:45.870 >>> print(''.join(results), end="")
2025-07-02 05:53:45.879 - abcDefghiJkl
2025-07-02 05:53:45.897 + abcdefGhijkl
2025-07-02 05:53:45.919 """
2025-07-02 05:53:45.928
2025-07-02 05:53:45.937 # don't synch up unless the lines have a similarity score of at
2025-07-02 05:53:45.950 # least cutoff; best_ratio tracks the best score seen so far
2025-07-02 05:53:45.961 best_ratio, cutoff = 0.74, 0.75
2025-07-02 05:53:45.972 >       cruncher = SequenceMatcher(self.charjunk)
2025-07-02 05:53:45.983
2025-07-02 05:53:45.992 /usr/lib/python3.11/difflib.py:915:
2025-07-02 05:53:46.000 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:46.006
2025-07-02 05:53:46.021 self = <difflib.SequenceMatcher object at [hex]>
2025-07-02 05:53:46.029 isjunk = <function IS_CHARACTER_JUNK at 0x7f5ee31af4c0>, a = '', b = ''
2025-07-02 05:53:46.036 autojunk = True
2025-07-02 05:53:46.042
2025-07-02 05:53:46.051 def __init__(self, isjunk=None, a='', b='', autojunk=True):
2025-07-02 05:53:46.060 """Construct a SequenceMatcher.
2025-07-02 05:53:46.073
2025-07-02 05:53:46.083 Optional arg isjunk is None (the default), or a one-argument
2025-07-02 05:53:46.091 function that takes a sequence element and returns true iff the
2025-07-02 05:53:46.100 element is junk.  None is equivalent to passing "lambda x: 0", i.e.
2025-07-02 05:53:46.108 no elements are considered to be junk.  For example, pass
2025-07-02 05:53:46.116 lambda x: x in " \\t"
2025-07-02 05:53:46.124 if you're comparing lines as sequences of characters, and don't
2025-07-02 05:53:46.131 want to synch up on blanks or hard tabs.
2025-07-02 05:53:46.137
2025-07-02 05:53:46.144 Optional arg a is the first of two sequences to be compared.  By
2025-07-02 05:53:46.151 default, an empty string.  The elements of a must be hashable.  See
2025-07-02 05:53:46.157 also .set_seqs() and .set_seq1().
2025-07-02 05:53:46.164
2025-07-02 05:53:46.175 Optional arg b is the second of two sequences to be compared.  By
2025-07-02 05:53:46.185 default, an empty string.  The elements of b must be hashable. See
2025-07-02 05:53:46.192 also .set_seqs() and .set_seq2().
2025-07-02 05:53:46.198
2025-07-02 05:53:46.207 Optional arg autojunk should be set to False to disable the
2025-07-02 05:53:46.218 "automatic junk heuristic" that treats popular elements as junk
2025-07-02 05:53:46.227 (see module documentation for more information).
2025-07-02 05:53:46.235 """
2025-07-02 05:53:46.242
2025-07-02 05:53:46.248 # Members:
2025-07-02 05:53:46.254 # a
2025-07-02 05:53:46.260 #      first sequence
2025-07-02 05:53:46.266 # b
2025-07-02 05:53:46.272 #      second sequence; differences are computed as "what do
2025-07-02 05:53:46.287 #      we need to do to 'a' to change it into 'b'?"
2025-07-02 05:53:46.295 # b2j
2025-07-02 05:53:46.303 #      for x in b, b2j[x] is a list of the indices (into b)
2025-07-02 05:53:46.311 #      at which x appears; junk and popular elements do not appear
2025-07-02 05:53:46.318 # fullbcount
2025-07-02 05:53:46.325 #      for x in b, fullbcount[x] == the number of times x
2025-07-02 05:53:46.331 #      appears in b; only materialized if really needed (used
2025-07-02 05:53:46.338 #      only for computing quick_ratio())
2025-07-02 05:53:46.346 # matching_blocks
2025-07-02 05:53:46.356 #      a list of (i, j, k) triples, where a[i:i+k] == b[j:j+k];
2025-07-02 05:53:46.362 #      ascending & non-overlapping in i and in j; terminated by
2025-07-02 05:53:46.369 #      a dummy (len(a), len(b), 0) sentinel
2025-07-02 05:53:46.374 # opcodes
2025-07-02 05:53:46.379 #      a list of (tag, i1, i2, j1, j2) tuples, where tag is
2025-07-02 05:53:46.385 #      one of
2025-07-02 05:53:46.391 #          'replace'   a[i1:i2] should be replaced by b[j1:j2]
2025-07-02 05:53:46.397 #          'delete'    a[i1:i2] should be deleted
2025-07-02 05:53:46.403 #          'insert'    b[j1:j2] should be inserted
2025-07-02 05:53:46.410 #          'equal'     a[i1:i2] == b[j1:j2]
2025-07-02 05:53:46.419 # isjunk
2025-07-02 05:53:46.431 #      a user-supplied function taking a sequence element and
2025-07-02 05:53:46.442 #      returning true iff the element is "junk" -- this has
2025-07-02 05:53:46.454 #      subtle but helpful effects on the algorithm, which I'll
2025-07-02 05:53:46.462 #      get around to writing up someday <0.9 wink>.
2025-07-02 05:53:46.469 #      DON'T USE!  Only __chain_b uses this.  Use "in self.bjunk".
2025-07-02 05:53:46.475 # bjunk
2025-07-02 05:53:46.481 #      the items in b for which isjunk is True.
2025-07-02 05:53:46.489 # bpopular
2025-07-02 05:53:46.501 #      nonjunk items in b treated as junk by the heuristic (if used).
2025-07-02 05:53:46.510
2025-07-02 05:53:46.519 self.isjunk = isjunk
2025-07-02 05:53:46.529 self.a = self.b = None
2025-07-02 05:53:46.541 self.autojunk = autojunk
2025-07-02 05:53:46.551 >       self.set_seqs(a, b)
2025-07-02 05:53:46.559
2025-07-02 05:53:46.566 /usr/lib/python3.11/difflib.py:182:
2025-07-02 05:53:46.573 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:46.579
2025-07-02 05:53:46.585 self = <difflib.SequenceMatcher object at [hex]>, a = '', b = ''
2025-07-02 05:53:46.592
2025-07-02 05:53:46.599 def set_seqs(self, a, b):
2025-07-02 05:53:46.607 """Set the two sequences to be compared.
2025-07-02 05:53:46.614
2025-07-02 05:53:46.623 >>> s = SequenceMatcher()
2025-07-02 05:53:46.633 >>> s.set_seqs("abcd", "bcde")
2025-07-02 05:53:46.640 >>> s.ratio()
2025-07-02 05:53:46.646 0.75
2025-07-02 05:53:46.651 """
2025-07-02 05:53:46.663
2025-07-02 05:53:46.672 self.set_seq1(a)
2025-07-02 05:53:46.683 >       self.set_seq2(b)
2025-07-02 05:53:46.698
2025-07-02 05:53:46.707 /usr/lib/python3.11/difflib.py:194:
2025-07-02 05:53:46.716 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 05:53:46.723
2025-07-02 05:53:46.732 self = <difflib.SequenceMatcher object at [hex]>, b = ''
2025-07-02 05:53:46.740
2025-07-02 05:53:46.748 def set_seq2(self, b):
2025-07-02 05:53:46.757 """Set the second sequence to be compared.
2025-07-02 05:53:46.764
2025-07-02 05:53:46.772 The first sequence to be compared is not changed.
2025-07-02 05:53:46.778
2025-07-02 05:53:46.790 >>> s = SequenceMatcher(None, "abcd", "bcde")
2025-07-02 05:53:46.800 >>> s.ratio()
2025-07-02 05:53:46.809 0.75
2025-07-02 05:53:46.816 >>> s.set_seq2("abcd")
2025-07-02 05:53:46.823 >>> s.ratio()
2025-07-02 05:53:46.829 1.0
2025-07-02 05:53:46.834 >>>
2025-07-02 05:53:46.840
2025-07-02 05:53:46.846 SequenceMatcher computes and caches detailed information about the
2025-07-02 05:53:46.853 second sequence, so if you want to compare one sequence S against
2025-07-02 05:53:46.859 many sequences, use .set_seq2(S) once and call .set_seq1(x)
2025-07-02 05:53:46.867 repeatedly for each of the other sequences.
2025-07-02 05:53:46.875
2025-07-02 05:53:46.885 See also set_seqs() and set_seq1().
2025-07-02 05:53:46.895 """
2025-07-02 05:53:46.906
2025-07-02 05:53:46.914 if b is self.b:
2025-07-02 05:53:46.922 return
2025-07-02 05:53:46.929 self.b = b
2025-07-02 05:53:46.942 self.matching_blocks = self.opcodes = None
2025-07-02 05:53:46.951 self.fullbcount = None
2025-07-02 05:53:46.959 >       self.__chain_b()
2025-07-02 05:53:46.967 E       RecursionError: maximum recursion depth exceeded
2025-07-02 05:53:46.979
2025-07-02 05:53:46.988 /usr/lib/python3.11/difflib.py:248: RecursionError
2025-07-02 05:53:46.996 ---------------------------- Captured stdout setup -----------------------------
2025-07-02 05:53:47.002 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 0x7f5ee31af4c0>, 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.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
2 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
3 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
4 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
5 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
6 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
7 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
8 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
9 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
10 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
11 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
12 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
13 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
14 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
15 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
16 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
17 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
18 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
19 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
20 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
21 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
22 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
23 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
24 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
25 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
26 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
27 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
28 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
29 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
30 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
31 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
32 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
33 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
34 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
35 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
36 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
37 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
38 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
39 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
40 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
41 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
42 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
43 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
44 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
45 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
46 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
47 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
48 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
49 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
50 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
51 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
52 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
53 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
54 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
55 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
56 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
57 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
58 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
59 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
60 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
61 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
62 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
63 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
64 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
65 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
66 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
67 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
68 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
69 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
70 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
71 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
72 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
73 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
74 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
75 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
76 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
77 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
78 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
79 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
80 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
81 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
82 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
83 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
84 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
85 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
86 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
87 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
88 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
89 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
90 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
91 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
92 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
93 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
94 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
95 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
96 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
97 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
98 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
99 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
100 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
101 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
102 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
103 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
104 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
105 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
106 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
107 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
108 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
109 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
110 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
111 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
112 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
113 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
114 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
115 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
116 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
117 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
118 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
119 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
120 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
121 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
122 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
123 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
124 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
125 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
126 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
127 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
128 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
129 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
130 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
131 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
132 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
133 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
134 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
135 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
136 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
137 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
138 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
139 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
140 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
141 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
142 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
143 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
144 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
145 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
146 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
147 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
148 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
149 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
150 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
151 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
152 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
153 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
154 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
155 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
156 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
157 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
158 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
159 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
160 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
161 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
162 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
163 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
164 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
165 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
166 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
167 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
168 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
169 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
170 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
171 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
172 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
173 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
174 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
175 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
176 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
177 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
178 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
179 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
180 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
181 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
182 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
183 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
184 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
185 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
186 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
187 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
188 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
189 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
190 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
191 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):