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)
def test_ancestor(self): """The matcher won't traverse into child nodes.""" parsed = matcher.parse_ast('~a', '<string>') self.assertIsNone( ast_matchers.UnaryOp( op=base_matchers.Unless(base_matchers.Anything())).match( matcher.MatchContext(parsed), parsed.tree.body[0]))
def test_has_hasparent(self, matcher_type): m = base_matchers.AllOf( base_matchers.Unless(ast_matchers.Add()), # Add can't stringify. matcher_type(ast_matchers.BinOp())) # even for IsOrHasAncestor, find_iter doesn't recurse into a matched node. self.assertEqual(self.get_all_match_strings(m, '1 + (2 + 3)'), ['1', '2 + 3'])
def test_nomatch(self): source = 'a\nb\n' m = base_matchers.Once(base_matchers.Unless(base_matchers.Anything())) self.assertEqual( self.get_all_match_strings(m, source), [], )
def test_inner_nomatches(self): parsed = matcher.parse_ast('xy = 2', '<string>') matches = list( matcher.find_iter( base_matchers.MatchesRegex( r'', base_matchers.Unless(base_matchers.Anything())), parsed)) self.assertEqual(matches, [])
def test_key(self): shared = 'shared' once_any = base_matchers.Once(base_matchers.Anything(), key=shared) once_never = base_matchers.Once(base_matchers.Unless( base_matchers.Anything()), key=shared) # once_never reuses the cached result of once_any, because they share a key. m = base_matchers.ItemsAre([once_any, once_never]) self.assertIsNotNone(m.match(_FAKE_CONTEXT.new(), [1, 2]))
def test_returns(self): self.assertEqual( self.get_all_match_strings( syntax_matchers.NamedFunctionDefinition( returns=base_matchers.Unless(None)), textwrap.dedent("""\ def f1(): pass def f2() -> None: pass def f3() -> int: return 3 """), ), ['def f2() -> None: pass', 'def f3() -> int: return 3'], )
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), )
def test_submatcher_fail(self): parsed, e = expression('~a') self.assertIsNone( ast_matchers.UnaryOp( op=base_matchers.Unless(base_matchers.Anything())).match( matcher.MatchContext(parsed), e))
# Matches any function returning Optional[T] for some T. _IN_FUNCTION_RETURNING_OPTIONAL = syntax_matchers.InNamedFunction( syntax_matchers.NamedFunctionDefinition(returns=base_matchers.AnyOf( syntax_matchers.ExprPattern('Optional[$_]'), syntax_matchers.ExprPattern('typing.Optional[$_]'), # TODO: May want to also include Union[None, ...]. # TODO: match type comments as well. ))) # Matches any returning that's not "return" or "return None" (which are two # different ast node values: ast.Return(value=None) and # ast.Return(value=ast.Name(id='None')) respectively) _NON_NONE_RETURN = matcher_.DebugLabeledMatcher( 'Non-none return', ast_matchers.Return(value=base_matchers.Unless( base_matchers.AnyOf(base_matchers.Equals(None), syntax_matchers.ExprPattern('None'))))) _NONE_RETURNS_FIXERS = [ 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.
def test_submatcher_wrong(self): self.assertIsNone( base_matchers.ItemsAre([ base_matchers.Unless(base_matchers.Anything()) ]).match(_FAKE_CONTEXT, [1]))
def test_submatch_rejects(self): self.assertIsNone( base_matchers.HasItem( -1, base_matchers.Unless(base_matchers.Anything())).match( _FAKE_CONTEXT, ['xyz']))
def test_unless_bindings(self): unless_bind = base_matchers.Unless( base_matchers.Bind('name', base_matchers.Anything())) self.assertEqual(unless_bind.bind_variables, set())
def test_double_unless_erasure(self): self.assertEqual( base_matchers.Unless( base_matchers.Unless(base_matchers.Bind('foo'))).match( _FAKE_CONTEXT, 1), matcher.MatchInfo(match.ObjectMatch(1)))
def test_unless(self): self.assertIsNone( base_matchers.Unless(base_matchers.Bind('foo')).match( _FAKE_CONTEXT, 1))
import ast from unittest import mock from absl.testing import absltest from absl.testing import parameterized from six.moves import range from refex import match from refex.python import evaluate from refex.python import matcher from refex.python import matcher_test_util from refex.python.matchers import ast_matchers from refex.python.matchers import base_matchers _NOTHING = base_matchers.Unless(base_matchers.Anything()) _FAKE_CONTEXT = matcher.MatchContext(matcher.parse_ast('', 'foo.py')) class BindTest(absltest.TestCase): def test_bind_name_invalid(self): with self.assertRaises(ValueError): base_matchers.Bind('__foo') def test_systembind_name_valid(self): base_matchers.SystemBind('__foo') def test_systembind_name_invalid(self): with self.assertRaises(ValueError): base_matchers.SystemBind('foo')