Exemple #1
0
 def test_discards_rewrite_error(self):
     # If we knew how to trigger a rewrite error, we'd just fix the bug, so let's
     # make one up.
     fx = fixer.CombiningPythonFixer([_search_replace_fixer('a', 'a')])
     with mock.patch.object(syntactic_template.PythonExprTemplate,
                            'substitute_match',
                            side_effect=formatting.RewriteError,
                            autospec=True):
         self.assertEqual(list(search.find_iter(fx, 'a', 'foo.py')), [])
Exemple #2
0
 def test_fixedpoint_insignificant_only(self):
     fx = fixer.CombiningPythonFixer([
         _search_replace_fixer('a', 'b', url='merged', significant=False),
         _search_replace_fixer('b', 'c', url='merged', significant=False),
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'a', 'foo.py', max_iterations=10)), [
             _substitution(significant=False,
                           category='refex.merged.not-significant')
         ])
Exemple #3
0
    def test_replace(self):
        fix = fixer.SimplePythonFixer(
            matcher=syntax_matchers.ExprPattern('$obj.attr'),
            replacement=syntactic_template.PythonTemplate(u'$obj.other'),
        )
        searcher = fixer.CombiningPythonFixer([fix])

        source = 'my_obj.attr + other_obj.attr'
        self.assertEqual('my_obj.other + other_obj.other',
                         search.rewrite_string(searcher, source, 'example.py'))
Exemple #4
0
 def test_fixedpoint_disjoint_merge(self):
     """Disjoint rewrites should be merged together when it helps."""
     fx = fixer.CombiningPythonFixer([
         _search_replace_fixer('a1', 'a2'),
         _search_replace_fixer('b1', 'b2'),
         _search_replace_fixer('a2, b2', 'final'),
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'a1, b1', 'foo.py', max_iterations=10)),
         [_substitution(replacements={'fixedpoint': 'final'})])
Exemple #5
0
 def test_doesnt_discard_unparseable_compound_stmt(self):
     fx = fixer.CombiningPythonFixer([
         fixer.SimplePythonFixer(message='ouch',
                                 matcher=ast_matchers.For(),
                                 replacement=formatting.ShTemplate('x x x'),
                                 url='')
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'for x in y: pass', 'foo.py')),
         [_substitution(message='ouch')])
Exemple #6
0
 def test_fixedpoint_merged_url(self):
     fx = fixer.CombiningPythonFixer([
         _search_replace_fixer('a', 'b', url='merged'),
         _search_replace_fixer('b', 'c', url='merged'),
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'a', 'foo.py', max_iterations=10)), [
             _substitution(
                 message='There are a few findings here:\n\na\n\nb',
                 url='merged')
         ])
Exemple #7
0
 def test_output_sorted(self):
     pyfixers = [
         _search_replace_fixer('a', 'x'),
         _search_replace_fixer('b', 'x')
     ]
     for python_fixers in [pyfixers, pyfixers[::-1]]:
         fx = fixer.CombiningPythonFixer(python_fixers)
         with self.subTest(reversed=python_fixers != pyfixers):
             self.assertEqual(
                 list(search.find_iter(fx, 'a + b', 'foo.py')),
                 [_substitution(message='a'),
                  _substitution(message='b')])
Exemple #8
0
    def test_discards_unparseable_expr(self):
        """Searching discards unparseable substitutions for expressions.

    (Note: this only happens during fixedpoint computation.)
    """
        fx = fixer.CombiningPythonFixer([
            fixer.SimplePythonFixer(message='',
                                    matcher=syntax_matchers.ExprPattern('a'),
                                    replacement=formatting.ShTemplate('x x x'),
                                    url='')
        ])
        self.assertEqual(
            list(search.find_iter(fx, 'a', 'foo.py', max_iterations=10)), [])
Exemple #9
0
 def test_fixedpoint_drop_redundant_messages(self):
     fx = fixer.CombiningPythonFixer([
         _search_replace_fixer('a', 'b', message='z', url='url_z'),
         _search_replace_fixer('b', 'c', message='z', url='url_z'),
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'a', 'foo.py', max_iterations=10)), [
             _substitution(
                 message='z',
                 matched_spans={'fixedpoint': (0, 1)},
                 primary_label='fixedpoint',
                 replacements={'fixedpoint': 'c'},
                 url='url_z',
             )
         ])
Exemple #10
0
 def test_examples_real(self):
     """Tests that the fixers do actually give the example replacement."""
     for fx in find_fixer.from_pattern('*').fixers:
         example = fx.example_fragment()
         example_replacement = fx.example_replacement()
         with self.subTest(fixer=type(fx).__name__,
                           example=example,
                           example_replacement=example_replacement):
             self.assertIsNotNone(example)
             substitutions = list(
                 search.find_iter(fixer.CombiningPythonFixer([fx]), example,
                                  'a.py'))
             self.assertLen(substitutions, 1)
             replaced = formatting.apply_substitutions(
                 example, substitutions)
             self.assertEqual(replaced, example_replacement)
Exemple #11
0
 def test_fixedpoint_significant(self, ab_significant, bc_sigificant):
     fx = fixer.CombiningPythonFixer([
         _search_replace_fixer('a',
                               'b',
                               url='merged',
                               significant=ab_significant),
         _search_replace_fixer('b',
                               'c',
                               url='merged',
                               significant=bc_sigificant),
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'a', 'foo.py', max_iterations=10)), [
             _substitution(significant=True,
                           category='refex.merged.significant')
         ])
Exemple #12
0
 def test_fixedpoint_drop_insignificant(self):
     fx = fixer.CombiningPythonFixer([
         _search_replace_fixer('a', 'b', url='url_a', significant=False),
         _search_replace_fixer('b', 'c', url='url_b', significant=True),
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'a', 'foo.py', max_iterations=10)), [
             substitution.Substitution(
                 message='b',
                 matched_spans={'fixedpoint': (0, 1)},
                 primary_label='fixedpoint',
                 replacements={'fixedpoint': 'c'},
                 url='url_b',
                 significant=True,
                 category='refex.merged.significant',
             )
         ])
Exemple #13
0
 def test_fixedpoint_nodrop_redundant_messages_with_different_urls(self):
     fx = fixer.CombiningPythonFixer([
         _search_replace_fixer('a', 'b', message='z', url='url_a'),
         _search_replace_fixer('b', 'c', message='z', url='url_b'),
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'a', 'foo.py', max_iterations=10)), [
             _substitution(
                 message=
                 'There are a few findings here:\n\nz\n(url_a)\n\nz\n(url_b)',
                 matched_spans={'fixedpoint': (0, 1)},
                 primary_label='fixedpoint',
                 replacements={'fixedpoint': 'c'},
                 url=
                 'https://refex.readthedocs.io/en/latest/guide/fixers/merged.html',
             )
         ])
Exemple #14
0
 def test_fixedpoint_keep_insignificant(self):
     fx = fixer.CombiningPythonFixer([
         _search_replace_fixer('a', 'b', url='url_a', significant=False),
         _search_replace_fixer('b', 'c', url='url_b', significant=False),
     ])
     self.assertEqual(
         list(search.find_iter(fx, 'a', 'foo.py', max_iterations=10)), [
             substitution.Substitution(
                 message=
                 'There are a few findings here:\n\na\n(url_a)\n\nb\n(url_b)',
                 matched_spans={'fixedpoint': (0, 1)},
                 primary_label='fixedpoint',
                 replacements={'fixedpoint': 'c'},
                 url=
                 'https://refex.readthedocs.io/en/latest/guide/fixers/merged.html',
                 significant=False,
                 category='refex.merged.not-significant',
             )
         ])
Exemple #15
0
class SimpleFixersTest(parameterized.TestCase):
    fixers = fixer.CombiningPythonFixer(
        correctness_fixers.SIMPLE_PYTHON_FIXERS)

    def test_skips_number_mod(self):
        before = 'y = 3 % (x)'
        self.assertEqual(before, _rewrite(self.fixers, before))

    @parameterized.parameters('(\nfoo)', '(foo\n)', '(foo\n.bar)')
    def test_skips_multiline_rhs(self, rhs):
        before = 'y = "hello %s" % {rhs}'.format(rhs=rhs)
        self.assertEqual(before, _rewrite(self.fixers, before))

    def test_skips_formatting_when_already_using_tuple(self):
        before = "y = 'hello %s' % (world,)"
        self.assertEqual(before, _rewrite(self.fixers, before))

    @parameterized.parameters('u', 'b', '')
    def test_changes_superfluous_parens_to_tuple_when_formatting(
            self, string_prefix):
        before = textwrap.dedent("""
       y = (
         {}'hello: %s\\n' % (thing.world))
    """).format(string_prefix)
        after = textwrap.dedent("""
       y = (
         {}'hello: %s\\n' % (thing.world,))
    """).format(string_prefix)
        self.assertEqual(after, _rewrite(self.fixers, before))

    @parameterized.parameters('None', 'True', 'False')
    def test_is_named_constant(self, constant):
        """Named constants aren't fixed by the is check: identity is guaranteed."""
        before = f'x is {constant}'
        self.assertEqual(before, _rewrite(self.fixers, before))

    @parameterized.parameters('42', '0x42', '0b01', '6.6', '1e1', '1j', '"s"',
                              'u"s"', 'b"s"')
    def test_is_unnamed_constant(self, constant):
        before = f'x is {constant}'
        after = f'x == {constant}'
        self.assertEqual(after, _rewrite(self.fixers, before))
Exemple #16
0
class MutableConstantFixers(parameterized.TestCase):

    mutable_constant_fixers = fixer.CombiningPythonFixer(
        idiom_fixers._MUTABLE_CONSTANT_FIXERS)

    @parameterized.named_parameters(
        ('set_literal', 'foo = {1}'),
        ('set_constructor', '_bar = set([1, 2])'),
        ('setcomp', 'mymod.bar = {x for x in y()}'),
    )
    def test_skips_non_constants(self, example):
        self.assertEqual(example,
                         _rewrite(self.mutable_constant_fixers, example))

    def test_multiline_fix(self):
        before = textwrap.dedent("""
      _MYCONST = {
                  1,  # Thing1
                  2,  # Thing2
                 }
    """)
        after = textwrap.dedent("""
      _MYCONST = frozenset({
                  1,  # Thing1
                  2,  # Thing2
                 })
    """)
        self.assertEqual(after, _rewrite(self.mutable_constant_fixers, before))

    def test_does_not_recurse(self):
        before = textwrap.dedent("""
      _MYCONST = frozenset(
               (a, b)
                for a
                in y()
                for b in set([1, 2, 3])
               )
     """)
        self.assertEqual(before, _rewrite(self.mutable_constant_fixers,
                                          before))
Exemple #17
0
def from_pattern(fixer_pattern: str) -> fixer.CombiningPythonFixer:
    """Provide a fixer that combines all the fixers specified in `fixer_pattern`.

  To get all the default fixers, pass '*'. Otherwise, to get a group of fixers
  by name, specify that name. (See _default_fixers & _extra_fixers).

  Args:
    fixer_pattern: The pattern of fixers to load.

  Returns:
    A PythonFixer.

  Raises:
    ValueError: The fixer pattern was not recognized.
  """
    # TODO: Allow you to do set operations like '*,-FixerNameHere', etc.
    # or something along those lines.
    if fixer_pattern in _extra_fixers:
        return fixer.CombiningPythonFixer(_extra_fixers[fixer_pattern])
    else:
        raise ValueError('Unknown fixer pattern %r: must provide one of: %s' %
                         (fixer_pattern, ', '.join(_extra_fixers.keys())))
Exemple #18
0
class NoneReturnFixerTest(absltest.TestCase):

    none_fixers = fixer.CombiningPythonFixer(idiom_fixers._NONE_RETURNS_FIXERS)

    def test_fix_non_void_function_with_bare_return(self):
        before = textwrap.dedent("""
      def func(a, b):
        if a:
         return 5
        elif b:
          print('a')
        return  # Nothing to return
    """)
        after = textwrap.dedent("""
      def func(a, b):
        if a:
         return 5
        elif b:
          print('a')
        return None  # Nothing to return
    """)
        self.assertEqual(after, _rewrite(self.none_fixers, before))

    def test_fix_void_function_with_return_none_and_bare_returns(self):
        before = textwrap.dedent("""
      def func(a, b):
        if a:
         return
        elif b:
          print('a')
          return None
        else:
          raise NotImplementedError
    """)
        after = textwrap.dedent("""
      def func(a, b):
        if a:
         return
        elif b:
          print('a')
          return
        else:
          raise NotImplementedError
    """)
        self.assertEqual(after, _rewrite(self.none_fixers, before))

    def test_fix_void_function_with_return_none_only(self):
        before = textwrap.dedent("""
      def func(a, b):
        if a:
         return None
        elif b:
          print('a')
          return None
        else:
          raise NotImplementedError
    """)
        after = textwrap.dedent("""
      def func(a, b):
        if a:
         return
        elif b:
          print('a')
          return
        else:
          raise NotImplementedError
    """)
        self.assertEqual(after, _rewrite(self.none_fixers, before))

    def test_skips_bad_void_functions_containing_other_functions(self):
        code = textwrap.dedent("""
      def func(a, b):
        if a:
         return None
        elif b:
          print('a')
        def inner():
          return 5
        return
    """)
        self.assertEmpty(
            list(search.find_iter(self.none_fixers, code, 'example.py')))

    def test_skips_bad_non_void_functions_containing_other_functions(self):
        code = textwrap.dedent("""
      def func(a, b):
        if a:
         return 5
        elif b:
          print('a')
          return
        def inner():
          return
        return inner
    """)
        self.assertEmpty(
            list(search.find_iter(self.none_fixers, code, 'example.py')))

    def test_skips_ok_funcs(self):
        code = textwrap.dedent("""
      def func1(a, b):
        if a:
         return 5
        elif b:
          print('a')
        return None

      def func2(a, b):
        if a:
         return
        elif b:
          print('a')
          return
        else:
          raise NotImplementedError
    """)
        self.assertEmpty(
            list(search.find_iter(self.none_fixers, code, 'example.py')))

    def test_single_return_none(self):
        before = textwrap.dedent("""
      def func(a, b):
        del a, b
        if 1:
          # do something here
          return None
    """)

        after = textwrap.dedent("""
      def func(a, b):
        del a, b
        if 1:
          # do something here
          return
    """)
        self.assertEqual(after, _rewrite(self.none_fixers, before))

    def test_fixes_methods(self):
        before = textwrap.dedent("""
      class A(object):
        def foo(self):
          if use_random():
            return None if random_bool() else 5
          elif something_else():
            return
          raise RuntimeError
    """)
        after = textwrap.dedent("""
      class A(object):
        def foo(self):
          if use_random():
            return None if random_bool() else 5
          elif something_else():
            return None
          raise RuntimeError
    """)
        self.assertEqual(after, _rewrite(self.none_fixers, before))

    @unittest.skipIf(six.PY2, 'Testing async functions')
    def test_fixes_async_functions(self):
        before = textwrap.dedent("""
      async def foo(self):
        if use_random():
          await asynio.sleep(2)
          return None if random_bool() else 5
        elif something_else():
          return
        else:
          return 6
    """)
        after = textwrap.dedent("""
      async def foo(self):
        if use_random():
          await asynio.sleep(2)
          return None if random_bool() else 5
        elif something_else():
          return None
        else:
          return 6
    """)
        self.assertEqual(after, _rewrite(self.none_fixers, before))

    def test_optional_return(self):
        """return None is fine if the function returns Optional[T]."""
        # TODO(b/117351081):  port this test to work on vanilla Python.
        example = textwrap.dedent("""
      import typing
      from typing import Optional

      def func() -> typing.Optional[int]:
        return None
      def func2() -> Optional[int]:
        return None
    """)

        self.assertEqual(example, _rewrite(self.none_fixers, example))
Exemple #19
0
class LoggingExceptionFixerTest(parameterized.TestCase):
    fixers = fixer.CombiningPythonFixer(idiom_fixers._LOGGING_FIXERS)

    @parameterized.named_parameters(
        ('function_call_without_args', 'dangerous_func()', 'dangerous_func'),
        ('function_call_with_args', 'dangerous_func(1, b=2)',
         'dangerous_func'),
        ('attribute_call_without_args', 'myobject.dangerous_func()',
         'myobject.dangerous_func'),
        ('attribute_call_with_args', 'myobject.dangerous_func(1, b=2)',
         'myobject.dangerous_func'),
        ('call_on_result_of_call',
         "myfunc().otherfunc(x, 'hello').dangerous_func()",
         "myfunc().otherfunc(x, 'hello').dangerous_func"),
        ('assignment_to_function_call', 'a = dangerous_func()',
         'dangerous_func'),
        ('assignment_to_attribute_call', 'a = a.b.c.dangerous_func()',
         'a.b.c.dangerous_func'),
        ('returned_function_call_without_args', 'return dangerous_func()',
         'dangerous_func'),
    )
    def test_rewrites_redundant_logging_exception_for(self, try_body,
                                                      failing_name):
        before = textwrap.dedent("""
      f = open('/tmp/myfile', 'w')
      try:
        %s
      except (ValueError, KeyError) as exc:
        logging.exception(exc)
        _record_error(exc)
      else:
        f.write('...')
      finally:
        f.close()
    """ % try_body)
        after = before.replace(
            'logging.exception(exc)',
            'logging.exception(\'Call to "%s" resulted in an error\')' %
            failing_name)
        self.assertEqual(after, _rewrite(self.fixers, before))

    @parameterized.named_parameters((
        'same_exception_id',
        'e',
    ), (
        'different_exception_id',
        'e2',
    ))
    def test_fixes_multiple_except_clauses(self, second_exception_id):
        before = textwrap.dedent("""
      try:
        dangerous_func()
      except KeyError as e:
        if 'mykey' in str(e):
          logging.exception(e)
        raise
      except Exception as {second_exception_id}:
        logging.exception({second_exception_id})
    """.format(second_exception_id=second_exception_id))

        after = textwrap.dedent("""
      try:
        dangerous_func()
      except KeyError as e:
        if 'mykey' in str(e):
          logging.exception('Call to "dangerous_func" resulted in an error')
        raise
      except Exception as {second_exception_id}:
        logging.exception('Call to "dangerous_func" resulted in an error')
    """.format(second_exception_id=second_exception_id))
        self.assertEqual(after, _rewrite(self.fixers, before))

    def test_nested_try_except(self):
        before = textwrap.dedent("""
    def foo():
      try:
        return dangerous_func()
      except Exception as e:
        try:
          f = e.foo
        except AttributeError as e:
          logging.exception(e)
    """)
        self.assertEqual(before, _rewrite(self.fixers, before))

    def test_skips_ambiguous_try_body(self):
        before = textwrap.dedent("""
    def foo():
      try:
        return dangerous_func()
        other_dangerous_func()
      except Exception as e:
        logging.exception(e)
    """)
        self.assertEqual(before, _rewrite(self.fixers, before))

    def test_skips_non_redundant_logging_exception_call(self):
        before = textwrap.dedent("""
      try:
        dangerous_func()
      except Exception as e:
        msg = "Something bad happened"
        logging.exception(msg)
    """)
        self.assertEqual(before, _rewrite(self.fixers, before))

    def test_skips_replacement_requiring_escaping(self):
        before = textwrap.dedent("""
      try:
        foo("abc", "xyz").dangerous_func()
      except Exception as e:
        logging.exception(e)
    """)
        self.assertEqual(before, _rewrite(self.fixers, before))
Exemple #20
0
class LoggingErrorFixerTest(parameterized.TestCase):
    fixers = fixer.CombiningPythonFixer(idiom_fixers._LOGGING_FIXERS)

    def test_nested_try_except(self):
        before = textwrap.dedent("""
    def foo(x, y):
      try:
        a = dangerous_func(x)
        b = other_func(y)
      except Exception as e:
        logging.error('Bad stuff: (%s, %d): %r', x, y, e)
        try:
          f = e.foo
        except AttributeError as e:
          logging.error('Error: %r', e)
        except IndexError as e2:
          logging.error('What happened? %s', e2)
    """)
        after = textwrap.dedent("""
    def foo(x, y):
      try:
        a = dangerous_func(x)
        b = other_func(y)
      except Exception as e:
        logging.exception('Bad stuff: (%s, %d): %r', x, y, e)
        try:
          f = e.foo
        except AttributeError as e:
          logging.exception('Error: %r', e)
        except IndexError as e2:
          logging.exception('What happened? %s', e2)
    """)
        self.assertEqual(after, _rewrite(self.fixers, before))

    def test_skips_exc_info(self):
        before = textwrap.dedent("""
      try:
        foo()
      except Exception as e:
        logging.error('Error: %r', e, exc_info=True)
    """)
        self.assertEqual(before, _rewrite(self.fixers, before))

    def test_skips_inner_unnamed_exception(self):
        before = textwrap.dedent("""
      try:
        foo()
      except Exception as e:
        try:
          bar()
        except:
          logging.error('bar() failed after foo() failed with: %r', e)
    """)
        self.assertEqual(before, _rewrite(self.fixers, before))

    def test_fixes_exception_as_message(self):
        before = textwrap.dedent("""
      try:
        foo()
      except Exception as e:
        logging.error(e)
    """)
        # NOTE(nmarrow): The replacement here is suboptimal and should be corrected
        # by the `logging.exception` fixer.
        after = textwrap.dedent("""
      try:
        foo()
      except Exception as e:
        logging.exception(e)
    """)
        self.assertEqual(after, _rewrite(self.fixers, before))
def _rewrite(fx, source):
  searcher = fixer.CombiningPythonFixer([fx])
  return search.rewrite_string(searcher, source, 'example.py')
Exemple #22
0
 def test_empty_fixers(self):
     fx = fixer.CombiningPythonFixer([])
     self.assertEqual(list(search.find_iter(fx, 'b', 'foo.py')), [])
Exemple #23
0
 def test_empty_results(self):
     fx = fixer.CombiningPythonFixer([_search_replace_fixer('a', 'x')])
     self.assertEqual(list(search.find_iter(fx, 'b', 'foo.py')), [])