def test_from_diff_multiple(self): self.assertEqual( matcher.MatchInfo.from_diff('metavar', 'a\nb\nc\n', '1\nb\n3\n'), matcher.MatchInfo( match.Match(), bindings={ 'metavar.0': matcher.BoundValue( match.SpanMatch( string='a\n', span=(0, 2), )), 'metavar.1': matcher.BoundValue( match.SpanMatch( string='c\n', span=(4, 6), )), }, replacements={ 'metavar.0': formatting.LiteralTemplate('1\n'), 'metavar.1': formatting.LiteralTemplate('3\n'), }, ), )
def from_diff( cls, metavariable_prefix: str, before: str, after: str, match=_match.Match(), ) -> 'MatchInfo': """Returns a minimized ``MatchInfo`` for the diff of before/after. Args: metavariable_prefix: the prefix for the named bindings/replacements. before: the entire file, before. after: the entire file, after. match: The match for the MatchInfo. Returns: A minimized ``MatchInfo`` for that diff. """ diffs = difflib.Differ().compare( before.splitlines(True), after.splitlines(True), ) # change the diffs to always end with an "unchanged" line, triggering # an append to `replacements`. diffs = itertools.chain(diffs, [' ']) start_offset = 0 end_offset = 0 replacement = [] replacements = [] for line in diffs: prefix = line[:2] line = line[2:] if prefix == ' ': if replacement or start_offset != end_offset: replacements.append( (start_offset, end_offset, replacement)) replacement = [] end_offset += len(line) start_offset = end_offset elif prefix == '+ ': replacement.append(line) elif prefix == '- ': end_offset += len(line) elif prefix == '? ': pass else: raise AssertionError(f'Unknown diff prefix: {prefix!r}') assert not replacement match_bindings = {} match_replacements = {} for i, (start, end, added_strings) in enumerate(replacements): metavar = f'{metavariable_prefix}.{i}' match_bindings[metavar] = BoundValue( _match.SpanMatch.from_text(before, (start, end))) match_replacements[metavar] = formatting.LiteralTemplate( ''.join(added_strings)) return cls(match, match_bindings, match_replacements)
def matcher_with_meta(self): if isinstance(self._replacement, formatting.Template): replacements = {search.ROOT_LABEL: self._replacement} else: replacements = self._replacement if self._message is not None: replacements[search.MESSAGE_LABEL] = formatting.LiteralTemplate( self._message) if self._url is not None: replacements[search.URL_LABEL] = formatting.LiteralTemplate( self._url) if self._category is not None: replacements[search.CATEGORY_LABEL] = formatting.LiteralTemplate( self._category) if self._significant: replacements[ search.SIGNIFICANT_LABEL] = formatting.LiteralTemplate( 'HACK_TRUE') return base_matchers.WithReplacements( base_matchers.SystemBind(search.ROOT_LABEL, self._matcher), replacements)
def test_from_diff_change(self): self.assertEqual( matcher.MatchInfo.from_diff('metavar', 'a\nb\n', 'a\nc\n'), matcher.MatchInfo( match.Match(), bindings={ 'metavar.0': matcher.BoundValue( match.SpanMatch( string='b\n', span=(2, 4), )) }, replacements={'metavar.0': formatting.LiteralTemplate('c\n')}, ), )