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 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 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 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_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'))
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')])
def _mutable_constant_fixer(matcher, replacement, **kwargs): return fixer.SimplePythonFixer( message= 'For constants, prefer immutable collections (like frozensets or tuples) to mutable collections (like sets or lists).', url= 'https://refex.readthedocs.io/en/latest/guide/fixers/mutable_constants.html', significant=False, category=_MUTABLE_CONSTANT_CATEGORY, matcher=matcher, replacement=replacement, **kwargs)
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)
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)), [])
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')) }), ),
fixer.SimplePythonFixer( message= 'If a function ever returns a value, all the code paths should have a return statement with a return value.', url= 'https://refex.readthedocs.io/en/latest/guide/fixers/return_none.html', significant=False, category=_NONE_RETURNS_CATEGORY, matcher=base_matchers.AllOf( syntax_matchers.StmtPattern('return'), syntax_matchers.InNamedFunction( _function_containing(_NON_NONE_RETURN)), # Nested functions are too weird to consider right now. # TODO: Add matchers to match only the first ancestor # function and a way to use IsOrHasDescendant that doesn't recurse # into nested functions. base_matchers.Unless( syntax_matchers.InNamedFunction( _function_containing( syntax_matchers.NamedFunctionDefinition())))), replacement=syntactic_template.PythonStmtTemplate('return None'), example_fragment=textwrap.dedent(""" def f(x): if x: return return -1 """), example_replacement=textwrap.dedent(""" def f(x): if x: return None return -1 """), ),