예제 #1
0
 def test_on_merge_differs(self):
     with self.assertRaises(matcher.MatchError):
         matcher.merge_bindings(
             {
                 'a':
                 matcher.BoundValue(0,
                                    on_merge=matcher.BindMerge.KEEP_FIRST)
             },
             {
                 'a':
                 matcher.BoundValue(0, on_merge=matcher.BindMerge.KEEP_LAST)
             },
         )
예제 #2
0
 def test_on_conflict_differs(self):
     with self.assertRaises(matcher.MatchError):
         matcher.merge_bindings(
             {
                 'a':
                 matcher.BoundValue(0,
                                    on_conflict=matcher.BindConflict.SKIP)
             },
             {
                 'a':
                 matcher.BoundValue(0,
                                    on_conflict=matcher.BindConflict.ERROR)
             },
         )
예제 #3
0
    def _match(self, context, candidate):
        matchinfo = self._subpattern.match(context, candidate)
        if matchinfo is None:
            return None

        span = matchinfo.match.span
        if span is None:
            return None  # can't search within this AST node.
        try:
            m = self._wrapped_regex.match(context.parsed_file.text, *span)
        except TypeError:
            return None
        if m is None:
            return None

        # TODO(b/118507248): Allow choosing a different binding type.
        bindings = matcher.merge_bindings(
            _re_match_to_bindings(self._wrapped_regex,
                                  context.parsed_file.text, m),
            matchinfo.bindings)

        if bindings is None:
            return None

        return attr.evolve(matchinfo, bindings=bindings)
예제 #4
0
    def _match(self, context, candidate):
        """Returns the submatcher's match, with a binding introduced by this Bind.

    Args:
      context: The match context.
      candidate: The candidate node to be matched.

    Returns:
      An extended :class:`~refex.python.matcher.MatchInfo` with the new binding
      specified in the constructor. Conflicts are merged according to
      ``on_conflict``. If there was no match, or on_conflict result in a skip,
      then this returns ``None``.

      See matcher.merge_bindings for more details.
    """
        result = self._submatcher.match(context, candidate)
        if result is None:
            return None

        bindings = matcher.merge_bindings(
            result.bindings, {
                self.name:
                matcher.BoundValue(result.match,
                                   on_conflict=self._on_conflict,
                                   on_merge=self._on_merge)
            })
        if bindings is None:
            return None
        return attr.evolve(result, bindings=bindings)
예제 #5
0
    def test_error_after_skip(self):
        """ERROR should still raise even if it is processed after a skip."""
        # This is a gnarly thing to test, because dicts aren't ordered.
        # Even if we used OrderedDict, ordered dict key views don't (and can't)
        # preserve order when they're intersected and so on.

        @attr.s()
        class OrderedKeyView(object):
            """Ordered fake key view for deterministic key iteration order."""
            keys = attr.ib()

            def __sub__(self, other_ordered_key_view):
                if self.keys != other_ordered_key_view.keys:
                    raise NotImplementedError(
                        "Test ordered key view doesn't support this.")
                return frozenset()

            def __and__(self, other_ordered_key_view):
                # can't preserve order otherwise...
                if self.keys != other_ordered_key_view.keys:
                    raise NotImplementedError("Can't preserve order.")
                return list(self.keys)

        @attr.s()
        class OrderedBindings(object):
            """OrderedDict wrapper that returns OrderedKeyView."""
            _dict = attr.ib(converter=collections.OrderedDict)

            def __getitem__(self, v):
                return self._dict[v]

            def keys(self):
                return OrderedKeyView(self._dict)

            viewkeys = keys

        bad_bindings = [
            ('a', matcher.BoundValue(0, matcher.BindConflict.SKIP)),
            ('b', matcher.BoundValue(0, matcher.BindConflict.ERROR)),
        ]

        for bindings in [bad_bindings, bad_bindings[::-1]]:
            with self.subTest(bindings=bindings):
                bindings = OrderedBindings(bindings)
                with self.assertRaises(matcher.MatchError):
                    matcher.merge_bindings(bindings, bindings)
예제 #6
0
    def _match(self, context, candidate):
        parent = candidate
        while True:
            parent = context.parsed_file.nav.get_parent(parent)
            if parent is None:
                return None

            m = self._first_ancestor.match(context, parent)
            if m is not None:
                break

        ancestor = m.match.matched
        m2 = self._also_matches.match(context, ancestor)
        if m2 is None:
            return None
        return matcher.MatchInfo(
            matcher.create_match(context.parsed_file, candidate),
            matcher.merge_bindings(m.bindings, m2.bindings))
예제 #7
0
 def test_empty(self):
     self.assertEqual(matcher.merge_bindings({}, {}), {})
예제 #8
0
 def test_noconflict(self):
     bindings = {'a': matcher.BoundValue(0)}
     self.assertEqual(matcher.merge_bindings({}, bindings), bindings)
예제 #9
0
 def test_skip(self):
     skip_bindings = {'a': matcher.BoundValue(0, matcher.BindConflict.SKIP)}
     self.assertIsNone(matcher.merge_bindings(skip_bindings, skip_bindings))
예제 #10
0
 def test_error(self):
     error_bindings = {
         'a': matcher.BoundValue(0, matcher.BindConflict.ERROR)
     }
     with self.assertRaises(matcher.MatchError):
         matcher.merge_bindings(error_bindings, error_bindings)