def assert_message_fixer(old_expr, new_expr, method, is_absl=False): """Fixer for assertTrue()/assertFalse()/etc. related error fixes. assertTrue(...) often produces less readable error information than alternative methods like assertEqual etc. Args: old_expr: a ExprPattern string for the expr to match new_expr: a template string for the replacement method: the method to link to in the docs. is_absl: Whether this is an absl method with absl docs. Returns: A fixer that replaces old_expr with new_expr. """ if is_absl: # absl doesn't have docs per se. url = f'https://github.com/abseil/abseil-py/search?q=%22def+{method}%22' else: url = f'https://docs.python.org/3/library/unittest.html#unittest.TestCase.{method}' dotdotdot = fixer.ImmutableDefaultDict(lambda _: '...') return fixer.SimplePythonFixer( message=( '%s is a more specific assertion, and may give more detailed error information than %s.' % (string.Template(new_expr).substitute(dotdotdot), string.Template(old_expr).substitute(dotdotdot))), matcher=syntax_matchers.ExprPattern(old_expr), replacement=syntactic_template.PythonExprTemplate(new_expr), url=url, category='pylint.g-generic-assert', )
def idiom_fixer( old_expr, new_expr, category, url='https://refex.readthedocs.io/en/latest/guide/fixers/idiom.html', ): """Fixer for making expressions "clearer" / less convoluted. This also helps normalize them for other fixers to apply. Args: old_expr: An ExprPattern string for the expr to match. new_expr: A string.Template string for the replacement. category: A category for the fix. url: An URL describing the fix. Returns: A fixer that replaces old_expr with new_expr. """ dotdotdot = fixer.ImmutableDefaultDict(lambda _: '...') return fixer.SimplePythonFixer( message=('This could be more Pythonic: %s -> %s.' % ((string.Template(old_expr).substitute(dotdotdot), string.Template(new_expr).substitute(dotdotdot)))), matcher=syntax_matchers.ExprPattern(old_expr), replacement=syntactic_template.PythonExprTemplate(new_expr), url=url, significant=False, category=category, )
def assert_alias_fixer( old_expr, new_expr, url='https://docs.python.org/3/library/unittest.html#deprecated-aliases'): """Fixer for deprecated unittest aliases. Args: old_expr: A string for an ExprPattern matching the target expr. new_expr: A string for a PythonExprTemplate to replace it with. url: The URL documenting the deprecation. Returns: A fixer that replaces old_expr with new_expr. """ dotdotdot = fixer.ImmutableDefaultDict(lambda _: '...') return fixer.SimplePythonFixer( message=('{old} is a deprecated alias for {new} in the unittest module.' .format( old=string.Template(old_expr).substitute(dotdotdot), new=string.Template(new_expr).substitute(dotdotdot))), matcher=syntax_matchers.ExprPattern(old_expr), replacement=syntactic_template.PythonExprTemplate(new_expr), url=url, significant=False, category='pylint.g-deprecated-assert', )
def main(): cli.run( runner=cli.RefexRunner( searcher=search.PyExprRewritingSearcher.from_matcher( # --mode=py.expr is equivalent to PyExprRewritingSearcher paired # with an ExprPattern. However, you can pass any matcher, not just # an ExprPattern. syntax_matchers.ExprPattern('hello'), { # Using ROOT_LABEL as a key is equivalent to --sub=world. # To get the equivalent of --named-sub=x=world, # it would 'x' as a key instead. # # The value type corresponds to the --sub-mode. While # refex on the command line defaults to picking the paired # --sub-mode that matches the --mode, here there are no # defaults and you must be explicit. # e.g. for unsafe textual substitutions, as with # --sub-mode=sh, you would use formatting.ShTemplate. search.ROOT_LABEL: syntactic_template.PythonExprTemplate('world') }, ), dry_run=False, ), files=sys.argv[1:], bug_report_url='<project bug report URL goes here>', )
def test_labeled_replacements_example_fragment(self): fx = fixer.SimplePythonFixer( message='', matcher=syntax_matchers.ExprPattern('$y'), replacement={'y': syntactic_template.PythonExprTemplate('$y')}, ) with self.assertRaises(TypeError): fx.example_replacement()
def test_nonpython_dollars_source(self, src): parsed = matcher.parse_ast(src) m = base_matchers.Bind('bound', ast_matchers.Call()) [matchinfo] = matcher.find_iter(m, parsed) self.assertEqual( src, syntactic_template.PythonExprTemplate('$bound').substitute_match( parsed, matchinfo.match, {'bound': matchinfo.match}))
def test_nonpython_dollars_dest(self): src = 'f' parsed = matcher.parse_ast(src) m = base_matchers.Bind('bound', ast_matchers.Name()) [matchinfo] = matcher.find_iter(m, parsed) self.assertEqual( 'f("$x")', syntactic_template.PythonExprTemplate( '$bound("$x")').substitute_match(parsed, matchinfo.match, {'bound': matchinfo.match}))
def _search_replace_fixer(search_expr, replace, message=None, url='', **kwargs): return fixer.SimplePythonFixer( message=message if message is not None else search_expr, matcher=syntax_matchers.ExprPattern(search_expr), replacement=syntactic_template.PythonExprTemplate(replace), url=url, category='TESTONLY', **kwargs)
class SumMatcherTest(absltest.TestCase): SEARCH_REPLACE = search.PyExprRewritingSearcher.from_matcher( example_custom_matcher.SumMatcher(), {search.ROOT_LABEL: syntactic_template.PythonExprTemplate('$sum')}, ) def test_sum_rewrite(self): self.assertEqual( search.rewrite_string(self.SEARCH_REPLACE, '1 + 2 + 3', 'filename.py'), '6') def test_sum_no_rewrite(self): self.assertEqual( search.rewrite_string(self.SEARCH_REPLACE, '1 + var', 'filename.py'), '1 + var')
def _dict_iter_fixer(method_name): return fixer.SimplePythonFixer( message=('dict.{method} is deprecated and does not exist in Python 3. ' 'Instead, import six and use six.{method}').format( method=method_name), matcher=with_six( syntax_matchers.ExprPattern('$x.{}()'.format(method_name), { 'x': base_matchers.Unless(syntax_matchers.ExprPattern('six')) }), ), replacement=syntactic_template.PythonExprTemplate( 'six.{}($x)'.format(method_name)), url='https://www.python.org/doc/sunset-python-2/', category='pylint.dict-iter-method', # 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), )
from refex.python import syntactic_template from refex.python.matchers import ast_matchers from refex.python.matchers import base_matchers from refex.python.matchers import syntax_matchers SIMPLE_PYTHON_FIXERS = [] # Disabled except when running in Python 2. def with_six(m): return syntax_matchers.WithTopLevelImport(m, 'six') _HAS_KEY_FIXER = fixer.SimplePythonFixer( message='dict.has_key() was removed in Python 3.', matcher=syntax_matchers.ExprPattern('$a.has_key($b)'), replacement=syntactic_template.PythonExprTemplate('$b in $a'), url='https://docs.python.org/3.1/whatsnew/3.0.html#builtins', significant=True, category='refex.modernize.dict_has_key', ) def _dict_iter_fixer(method_name): return fixer.SimplePythonFixer( message=('dict.{method} is deprecated and does not exist in Python 3. ' 'Instead, import six and use six.{method}').format( method=method_name), matcher=with_six( syntax_matchers.ExprPattern('$x.{}()'.format(method_name), { 'x': base_matchers.Unless(syntax_matchers.ExprPattern('six'))
def test_empty_expr(self): with self.assertRaises(ValueError): syntactic_template.PythonExprTemplate('')
def test_variables(self, template, expected_variables): self.assertEqual( syntactic_template.PythonExprTemplate(template).variables, expected_variables)