def tree_matches(self, template): if is_ast_like(self.tree, ast.parse(template)): return True if is_ast_like(ast.parse(self.input.lower()), ast.parse(template.lower())): return dict( message= "Python is case sensitive! That means that small and capital letters " "matter and changing them changes the meaning of the program. The strings " "`'hello'` and `'Hello'` are different, as are the variable names " "`word` and `Word`.")
def visit_Call(self, node): positional_final_wildcard = False for i, n in enumerate(node.args): if astcheck.is_ast_like(n, ast.Name(id=MULTIWILDCARD_NAME)): if i + 1 == len(node.args): # Last positional argument - wildcard may extend to kwargs positional_final_wildcard = True node.args = self._visit_list(node.args[:i]) + astcheck.listmiddle() \ + self._visit_list(node.args[i+1:]) # Don't try to handle multiple multiwildcards break kwargs_are_subset = False if positional_final_wildcard and node.starargs is None: del node.starargs # Accept any (or none) *args # f(a, ??) -> wildcarded kwargs as well kwargs_are_subset = True if kwargs_are_subset or any(k.arg == MULTIWILDCARD_NAME for k in node.keywords): template_keywords = [ self.visit(k) for k in node.keywords if k.arg != MULTIWILDCARD_NAME ] def kwargs_checker(sample_keywords, path): sample_kwargs = {k.arg: k.value for k in sample_keywords} for k in template_keywords: if k.arg == MULTIWILDCARD_NAME: continue if k.arg in sample_kwargs: astcheck.assert_ast_like(sample_kwargs[k.arg], k.value, path + [k.arg]) else: raise astcheck.ASTMismatch(path, '(missing)', 'keyword arg %s' % k.arg) if template_keywords: node.keywords = kwargs_checker else: # Shortcut if there are no keywords to check del node.keywords # Accepting arbitrary keywords, so don't check absence of **kwargs if node.kwargs is None: del node.kwargs # In block contexts, we want to avoid checking empty lists (for optional # nodes), but here, an empty list should mean that there are no # arguments in that group. So we need to override the behaviour in # generic_visit if node.args == []: node.args = must_not_exist_checker if getattr(node, 'keywords', None) == []: node.keywords = must_not_exist_checker return self.generic_visit(node)
def scan_ast(self, tree): """Walk an AST and yield nodes matching pattern. :param ast.AST tree: The AST in which to search """ nodetype = type(self.pattern) for node in ast.walk(tree): if isinstance(node, nodetype) and astcheck.is_ast_like(node, self.pattern): yield node
def scan_ast(self, tree): """Walk an AST and yield nodes matching pattern. :param ast.AST tree: The AST in which to search """ nodetype = type(self.pattern) for node in ast.walk(tree): if isinstance(node, nodetype) and astcheck.is_ast_like( node, self.pattern): yield node
def search_ast(node, template, predicate=lambda n: True): """ Returns the number of descendants of `node` that match `template` (either a type or tuple that is passed to `isinstance`, or a partial AST that is passed to `is_ast_like`) and satisfy the optional predicate. """ return sum( (isinstance(child, template) if isinstance(template, ( type, tuple)) else is_ast_like(child, template)) and predicate(child) and child != node for child in ast.walk(node))
def visit_Call(self, node): positional_final_wildcard = False for i, n in enumerate(node.args): if astcheck.is_ast_like(n, ast.Name(id=MULTIWILDCARD_NAME)): if i+1 == len(node.args): # Last positional argument - wildcard may extend to kwargs positional_final_wildcard = True node.args = self._visit_list(node.args[:i]) + astcheck.listmiddle() \ + self._visit_list(node.args[i+1:]) # Don't try to handle multiple multiwildcards break kwargs_are_subset = False if positional_final_wildcard and node.starargs is None: del node.starargs # Accept any (or none) *args # f(a, ??) -> wildcarded kwargs as well kwargs_are_subset = True if kwargs_are_subset or any(k.arg==MULTIWILDCARD_NAME for k in node.keywords): template_keywords = [self.visit(k) for k in node.keywords if k.arg != MULTIWILDCARD_NAME] def kwargs_checker(sample_keywords, path): sample_kwargs = {k.arg: k.value for k in sample_keywords} for k in template_keywords: if k.arg == MULTIWILDCARD_NAME: continue if k.arg in sample_kwargs: astcheck.assert_ast_like(sample_kwargs[k.arg], k.value, path+[k.arg]) else: raise astcheck.ASTMismatch(path, '(missing)', 'keyword arg %s' % k.arg) if template_keywords: node.keywords = kwargs_checker else: # Shortcut if there are no keywords to check del node.keywords # Accepting arbitrary keywords, so don't check absence of **kwargs if node.kwargs is None: del node.kwargs # In block contexts, we want to avoid checking empty lists (for optional # nodes), but here, an empty list should mean that there are no # arguments in that group. So we need to override the behaviour in # generic_visit if node.args == []: node.args = must_not_exist_checker if getattr(node, 'keywords', None) == []: node.keywords = must_not_exist_checker return self.generic_visit(node)
def immunise_setuptools(): ei_file = easy_install.__file__ if not os.access(ei_file, os.R_OK|os.W_OK): print("Don't have access to {}, not changing".format(ei_file)) return # Don't try this at home lines, first_line_no = inspect.getsourcelines( easy_install.PthDistributions.save) mod = ast.parse("".join(lines).lstrip()) pattern = ast.Assign(targets=[ast.Name(id='data')], value=ast.BinOp(left=ast.Str(), op=ast.Mod(), right=ast.Name(id='data'))) for astpath, node in _walk_with_path(mod): if astcheck.is_ast_like(node, pattern): break else: print("Leaving easy_install module untouched") return starting_line = first_line_no + node.lineno - 1 parent = _resolve_ast_path(astpath[:-2], mod) # -2 for ('body', ix) if astpath[-1] == len(parent.body) - 1: print("PthDistributions.save not as expected, leaving untouched") return print("Immunising", ei_file) next_node = parent.body[astpath[-1]+1] finishing_line = first_line_no + next_node.lineno - 1 print("Cut lines:", starting_line, finishing_line) with open(ei_file) as f: contents = f.readlines() #print(contents[starting_line-1:finishing_line-1]) contents[starting_line-1:finishing_line-1] = [] with open(ei_file, 'w') as f: f.writelines(contents)
def search_ast(node, template): return any(is_ast_like(child, template) for child in ast.walk(node))
def truncated_trees_match(self, input_tree, program_tree): input_tree = ast.Module( body=input_tree.body[:len(program_tree.body)], type_ignores=[], ) return is_ast_like(input_tree, program_tree)
def test_matching(self): assert_ast_like(sample1, template1) assert is_ast_like(sample1, template1) assert_ast_like(sample2, template2) assert is_ast_like(sample2, template2)
def test_name_or_attr_correct(self): assert_ast_like(sample4, template4) assert is_ast_like(sample4, template4)
def test_matching(self): assert_ast_like(sample3, template3) assert is_ast_like(sample3, template3) assert isinstance(template3.body.front[0], ast.Delete) assert isinstance(template3.body.back[0], ast.Delete)
def test_wrong_plain_list(self): with self.assertRaisesRegexp(astcheck.ASTPlainListMismatch, re.escape("Expected: ['x', 'y']")): assert_ast_like(sample2, template2_wronglist) assert not is_ast_like(sample2, template2_wronglist)
def test_wrong_plain_value(self): with self.assertRaisesRegexp(astcheck.ASTPlainObjMismatch, "'d' instead of 'e'"): assert_ast_like(sample1, template1_wrongvalue) assert not is_ast_like(sample1, template1_wrongvalue)
def truncated_trees_match(self, input_tree, program_tree): del input_tree.body[len(program_tree.body):] return is_ast_like(input_tree, program_tree)
def _is_multiwildcard(n): return astcheck.is_ast_like( n, ast.Expr(value=ast.Name(id=MULTIWILDCARD_NAME)))
def test_wrongnode(self): with self.assertRaises(astcheck.ASTNodeTypeMismatch): assert_ast_like(sample1, template1_wrongnode) assert not is_ast_like(sample1, template1_wrongnode)
def test_wrong_nodelist(self): with self.assertRaisesRegexp(astcheck.ASTNodeListMismatch, re.escape("5 node(s) instead of 4")): assert_ast_like(sample1, template1_wrongnodelist) assert not is_ast_like(sample1, template1_wrongnodelist)
def check(self): return (is_ast_like( self.tree, ast.Module( body=[ast.Assign(targets=[ast.Name(id='your_name')])])) and isinstance(self.console.locals.get('your_name'), str))
def _is_multiwildcard(n): return astcheck.is_ast_like(n, ast.Expr(value=ast.Name(id=MULTIWILDCARD_NAME)))