Пример #1
0
    def test_matches(self):
        parsed = matcher.parse_ast('var_hello = 42', '<string>')
        m = base_matchers.AllOf(base_matchers.FileMatchesRegex('hello'),
                                ast_matchers.Num())

        matches = list(matcher.find_iter(m, parsed))

        self.assertLen(matches, 1)
Пример #2
0
    def test_doesnt_match(self):
        parsed = matcher.parse_ast('hi = 42', '<string>')
        m = base_matchers.AllOf(base_matchers.FileMatchesRegex('hello'),
                                ast_matchers.Num())

        matches = list(matcher.find_iter(m, parsed))

        self.assertEqual(matches, [])
Пример #3
0
 def test_nested_invert(self, source, matches):
     self.assertEqual(
         self.get_all_match_strings(
             base_matchers.MaybeWrapped(
                 ast_matchers.Num(),
                 lambda i: ast_matchers.UnaryOp(op=ast_matchers.Invert(),
                                                operand=i)), source),
         matches)
Пример #4
0
 def test_eq(self):
     """Different RecursivelyWrapped nodes with the same structure are equal."""
     x, y = [
         base_matchers.RecursivelyWrapped(
             ast_matchers.Num(),
             lambda i: ast_matchers.UnaryOp(op=ast_matchers.Invert(),
                                            operand=i)) for _ in range(2)
     ]
     self.assertEqual(x, y)
Пример #5
0
 def test_bindings(self):
     parsed = matcher.parse_ast('2', '<string>')
     matches = list(
         matcher.find_iter(
             base_matchers.MatchesRegex(r'(?P<var>.)', ast_matchers.Num()),
             parsed))
     self.assertLen(matches, 1)
     [m] = matches
     self.assertIn('var', m.bindings)
     self.assertEqual(m.bindings['var'].value.span, (0, 1))
Пример #6
0
    def test_recursive_bindings(self):
        """Recursive matchers cover both recursive/base cases in .bind_variables.

    If this test fails with a RecursionError, that is a problem.
    """
        m = base_matchers.RecursivelyWrapped(
            base_matchers.Bind('base_case', ast_matchers.Num()),
            lambda i: base_matchers.Bind(
                'recursive_case',
                ast_matchers.UnaryOp(op=ast_matchers.Invert(), operand=i)))
        self.assertEqual(m.bind_variables, {'base_case', 'recursive_case'})
Пример #7
0
 def test_repr(self):
     # It turns out attrs automatically handles cyclic objects, but we can
     # improve on its repr.
     self.assertEqual(
         repr(
             base_matchers.RecursivelyWrapped(
                 ast_matchers.Num(),
                 lambda i: ast_matchers.UnaryOp(op=ast_matchers.Invert(),
                                                operand=i))),
         'RecursivelyWrapped(_matchers=(Num(n=Anything()),'
         ' UnaryOp(op=Invert(), operand=_Recurse(...))))')
Пример #8
0
    def test_ne(self):
        base = ast_matchers.Num()
        recursive = lambda m: ast_matchers.UnaryOp(op=ast_matchers.Invert(),
                                                   operand=m)
        example_matcher = base_matchers.RecursivelyWrapped(base, recursive)

        different_1 = base_matchers.RecursivelyWrapped(
            base_matchers.Unless(base), recursive)
        different_2 = base_matchers.RecursivelyWrapped(
            base, lambda m: base_matchers.Unless(recursive(m)))

        for different_matcher in [different_1, different_2]:
            self.assertNotEqual(example_matcher, different_matcher)
Пример #9
0
    def test_multi_regex(self):
        """Tests that the lazy dictionary doesn't walk over itself or something."""
        parsed = matcher.parse_ast('var_hello = 42', '<string>')
        m = base_matchers.AllOf(
            base_matchers.FileMatchesRegex('var'),
            base_matchers.FileMatchesRegex('hello'),
            base_matchers.FileMatchesRegex('42'),
            base_matchers.FileMatchesRegex(r'\Avar_hello = 42\Z'),
            ast_matchers.Num())

        matches = list(matcher.find_iter(m, parsed))

        self.assertLen(matches, 1)
Пример #10
0
  def test_positional_arguments(self):
    """Positional arguments are reserved for later use.

    Clang AST matchers use them as an implicit forAll, for example. This seems
    useful. But the default used by attrs is to define all the fields as
    positional arguments as well, and this is borderline useless -- nobody is
    going to remember what the order of the fields is. So it is forbidden, to
    ensure nobody relies on it. People might otherwise be tempted by e.g. Num,
    which has only one parameter. (Num(3) is readable, but still banned.)
    """
    with self.assertRaises(TypeError):
      ast_matchers.Num(3)  # n=3 is fine though.

    if not six.PY2:
      with self.assertRaises(TypeError):
        ast_matchers.Constant(3)  # value=3 is fine though.
Пример #11
0
 def test_num_non_number(self, non_number):
   parsed = matcher.parse_ast(non_number, '<string>')
   self.assertIsNone(ast_matchers.Num().match(
       matcher.MatchContext(parsed), parsed.tree.body[0].value))
Пример #12
0
 def test_nomatch(self, matcher_type):
     m = matcher_type(ast_matchers.Num())
     self.assertEqual(self.get_all_match_strings(m, 'x + y'), [])
Пример #13
0
        # Must define manually due to the extra restrictions on the pattern.
        example_fragment='import six; x.{}()'.format(method_name),
        example_replacement='import six; six.{}(x)'.format(method_name),
    )


if six.PY2:
    SIMPLE_PYTHON_FIXERS.extend([
        _HAS_KEY_FIXER,
        fixer.SimplePythonFixer(
            message=(
                'long literals are deprecated and will not work in Python 3.'
                ' Regular int literals without the L suffix will generally'
                ' work just as well.'),
            matcher=base_matchers.MatchesRegex(r'(?P<num>.+)[lL]',
                                               ast_matchers.Num()),
            replacement=formatting.ShTemplate('$num'),
            url='https://www.python.org/doc/sunset-python-2/',
            category='pylint.long-suffix',
            example_fragment='42L',
            example_replacement='42',
        ),
        fixer.SimplePythonFixer(
            message=(
                'Octal integer literals using 0NNN (with a leading 0) are'
                ' deprecated and will not work in Python 3. Instead, use the'
                ' 0oNNN syntax.'),
            # Have to be very careful here -- while 04 will not parse in Python 3,
            # expressions like 04.0 are valid in both 2 and 3 and not octal.
            matcher=base_matchers.MatchesRegex(
                r'(?P<prefix>[-+]?)0(?P<num>\d+)', ast_matchers.Num()),
Пример #14
0
class ConstantTest(parameterized.TestCase):
  """In Python 3.8, the AST hierarchy for constants was changed dramatically.

  To preserve compatibility with <3.8, we implement compatibility shims that
  reflect the old API. They're also potentially just plain handy.
  """

  @parameterized.parameters(
      ast_matchers.Num(n=0), ast_matchers.Num(n=0.0), ast_matchers.Num(n=0j),
      ast_matchers.Num())
  def test_num(self, num_matcher):
    for s in '0', '0.0', '0j':
      with self.subTest(s=s):
        parsed = matcher.parse_ast(s, '<string>')
        self.assertIsNotNone(
            num_matcher.match(
                matcher.MatchContext(parsed), parsed.tree.body[0].value))

  @parameterized.parameters('"string"', 'b"bytes"', 'True', 'None')
  def test_num_non_number(self, non_number):
    parsed = matcher.parse_ast(non_number, '<string>')
    self.assertIsNone(ast_matchers.Num().match(
        matcher.MatchContext(parsed), parsed.tree.body[0].value))

  @unittest.skipIf(six.PY2, 'ast.Bytes is python 3 only')
  @parameterized.parameters(({'s': b''},), ({},))
  def test_bytes(self, kwargs):
    bytes_matcher = ast_matchers.Bytes(**kwargs)  # hack for py2
    parsed = matcher.parse_ast('b""', '<string>')
    self.assertIsNotNone(
        bytes_matcher.match(
            matcher.MatchContext(parsed), parsed.tree.body[0].value))

  @unittest.skipIf(six.PY2, 'ast.Bytes is python 3 only')
  def test_bytes_non_bytes(self):
    parsed = matcher.parse_ast('"string"', '<string>')
    self.assertIsNone(ast_matchers.Bytes().match(
        matcher.MatchContext(parsed), parsed.tree.body[0].value))

  @parameterized.parameters(ast_matchers.Str(s=''), ast_matchers.Str())
  def test_string(self, str_matcher):
    parsed = matcher.parse_ast('""', '<string>')
    self.assertIsNotNone(
        str_matcher.match(
            matcher.MatchContext(parsed), parsed.tree.body[0].value))

  def test_string_non_string(self):
    parsed = matcher.parse_ast('2', '<string>')
    self.assertIsNone(ast_matchers.Str().match(
        matcher.MatchContext(parsed), parsed.tree.body[0].value))

  @unittest.skipIf(six.PY2, '`...` is python 3 only')
  def test_ellipsis(self):
    parsed = matcher.parse_ast('...', '<string>')
    self.assertIsNotNone(ast_matchers.Ellipsis().match(
        matcher.MatchContext(parsed), parsed.tree.body[0].value))

  def test_ellipsis_non_ellipsis(self):
    parsed = matcher.parse_ast('1', '<string>')
    self.assertIsNone(ast_matchers.Ellipsis().match(
        matcher.MatchContext(parsed), parsed.tree.body[0].value))

  @unittest.skipIf(six.PY2, 'NameConstant is python 3 only')
  @parameterized.parameters(True, False, None)
  def test_named_constant(self, constant):
    parsed = matcher.parse_ast(str(constant), '<string>')
    for m in ast_matchers.NameConstant(), ast_matchers.NameConstant(
        value=constant):
      with self.subTest(matcher=m):
        self.assertIsNotNone(
            m.match(matcher.MatchContext(parsed), parsed.tree.body[0].value))

  @unittest.skipIf(six.PY2, 'NameConstant is python 3 only')
  def test_named_constant_non_named_constant(self):
    parsed = matcher.parse_ast('1', '<string>')
    self.assertIsNone(ast_matchers.NameConstant().match(
        matcher.MatchContext(parsed), parsed.tree.body[0].value))
Пример #15
0
 def test_nonchild_descendant(self, matcher_type):
     m = base_matchers.AllOf(ast_matchers.Call(),
                             matcher_type(ast_matchers.Num()))
     self.assertEqual(self.get_all_match_strings(m, 'foo(x + 1)'),
                      ['foo(x + 1)'])
Пример #16
0
 def test_nonchild_descendant_haschild(self):
     m = base_matchers.AllOf(ast_matchers.Call(),
                             syntax_matchers.HasChild(ast_matchers.Num()))
     self.assertEqual(self.get_all_match_strings(m, 'foo(x + 1)'), [])
Пример #17
0
 def test_nonparent_ancestor(self, matcher_type):
     m = base_matchers.AllOf(ast_matchers.Num(),
                             matcher_type(ast_matchers.Call()))
     self.assertEqual(self.get_all_match_strings(m, 'foo(x + 1)'), ['1'])
Пример #18
0
 def test_nonparent_ancestor_hasparent(self):
     m = base_matchers.AllOf(ast_matchers.Num(),
                             syntax_matchers.HasParent(ast_matchers.Call()))
     self.assertEqual(self.get_all_match_strings(m, 'foo(x + 1)'), [])
Пример #19
0
 def test_has_haschild(self, matcher_type):
     m = matcher_type(ast_matchers.Num())
     # even for IsOrHasDescendant, find_iter doesn't recurse into a matched node.
     self.assertEqual(self.get_all_match_strings(m, 'x + 1'), ['x + 1'])