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 visit_arguments(self, node): positional_final_wildcard = False for i, a in enumerate(node.args): if a.arg == MULTIWILDCARD_NAME: from_end = len(node.args) - (i + 1) if from_end == 0: # Last positional argument - wildcard may extend to other groups positional_final_wildcard = True args = self._visit_list(node.args[:i]) + astcheck.listmiddle() \ + self._visit_list(node.args[i+1:]) break else: if node.args: args = self._visit_list(node.args) else: args = must_not_exist_checker defaults = [ (a.arg, self.visit(d)) for a, d in zip(node.args[-len(node.defaults):], node.defaults) if a.arg not in {WILDCARD_NAME, MULTIWILDCARD_NAME} ] if node.vararg is None: if positional_final_wildcard: vararg = None else: vararg = must_not_exist_checker else: vararg = self.visit(node.vararg) kwonly_args_dflts = [(self.visit(a), (d if d is None else self.visit(d))) for a, d in zip(node.kwonlyargs, node.kw_defaults) if a.arg != MULTIWILDCARD_NAME] koa_subset = (positional_final_wildcard and vararg is None and (not node.kwonlyargs)) \ or any(a.arg == MULTIWILDCARD_NAME for a in node.kwonlyargs) if node.kwarg is None: if koa_subset: kwarg = None else: kwarg = must_not_exist_checker else: kwarg = self.visit(node.kwarg) return ArgsDefChecker(args=args, defaults=defaults, vararg=vararg, kwonly_args_dflts=kwonly_args_dflts, koa_subset=koa_subset, kwarg=kwarg)
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 visit_arguments(self, node): positional_final_wildcard = False for i, a in enumerate(node.args): if a.arg == MULTIWILDCARD_NAME: from_end = len(node.args) - (i+1) if from_end == 0: # Last positional argument - wildcard may extend to other groups positional_final_wildcard = True args = self._visit_list(node.args[:i]) + astcheck.listmiddle() \ + self._visit_list(node.args[i+1:]) break else: if node.args: args = self._visit_list(node.args) else: args = must_not_exist_checker defaults = [(a.arg, self.visit(d)) for a,d in zip(node.args[-len(node.defaults):], node.defaults) if a.arg not in {WILDCARD_NAME, MULTIWILDCARD_NAME}] if node.vararg is None: if positional_final_wildcard: vararg = None else: vararg = must_not_exist_checker else: vararg = self.visit(node.vararg) kwonly_args_dflts = [(self.visit(a), (d if d is None else self.visit(d))) for a, d in zip(node.kwonlyargs, node.kw_defaults) if a.arg != MULTIWILDCARD_NAME] koa_subset = (positional_final_wildcard and vararg is None and (not node.kwonlyargs)) \ or any(a.arg == MULTIWILDCARD_NAME for a in node.kwonlyargs) if node.kwarg is None: if koa_subset: kwarg = None else: kwarg = must_not_exist_checker else: kwarg = self.visit(node.kwarg) return ArgsDefChecker(args=args, defaults=defaults, vararg=vararg, kwonly_args_dflts=kwonly_args_dflts, koa_subset=koa_subset, kwarg=kwarg)
def prune_wildcard_body(self, node, attrname, must_exist=False): """Prunes a code block (e.g. function body) if it is a wildcard""" body = getattr(node, attrname, []) def _is_multiwildcard(n): return astcheck.is_ast_like(n, ast.Expr(value=ast.Name(id=MULTIWILDCARD_NAME))) if len(body) == 1 and _is_multiwildcard(body[0]): setattr(node, attrname, must_exist_checker) return # Find a ?? node within the block, and replace it with listmiddle for i, n in enumerate(body): if _is_multiwildcard(n): newbody = body[:i] + astcheck.listmiddle() + body[i+1:] setattr(node, attrname, newbody)
def prune_wildcard_body(self, node, attrname, must_exist=False): """Prunes a code block (e.g. function body) if it is a wildcard""" body = getattr(node, attrname, []) def _is_multiwildcard(n): return astcheck.is_ast_like( n, ast.Expr(value=ast.Name(id=MULTIWILDCARD_NAME))) if len(body) == 1 and _is_multiwildcard(body[0]): setattr(node, attrname, must_exist_checker) return # Find a ?? node within the block, and replace it with listmiddle for i, n in enumerate(body): if _is_multiwildcard(n): newbody = body[:i] + astcheck.listmiddle() + body[i + 1:] setattr(node, attrname, newbody)
print(y) c * 2 a = a*2 a = a - 3 x * 2 x += 1 sum(d + [1]) sysmod.version # modules shouldn't become parameters. """ template = ast.Module(body=[ ast.FunctionDef( name="foobar", args=ast.arguments( args=[mkarg(name) for name in ['z', 'y', 'a', 'x', 'd']]), body=listmiddle() + [ ast.Return(value=ast.Tuple(elts=[ ast.Name(id=n, ctx=ast.Load()) for n in ['b', 'subfunc', 'a', 'x'] ])) ]), ast.Assign(targets=[ ast.Tuple(ctx=ast.Store(), names=[ ast.Name(id=id, ctx=ast.Store()) for id in ['b', 'subfunc', 'a', 'x'] ]) ], value=ast.Call(func=ast.Name(id="foobar", ctx=ast.Load()), args=[ ast.Name(id=id, ctx=ast.Load())
assert not is_ast_like(sample2, template2_wronglist) def test_format_path(): assert format_path(['tree', 'body', 0, 'name']) == 'tree.body[0].name' sample3_code = """ del a del b del c del d """ sample3 = ast.parse(sample3_code) template3 = ast.Module(body=[ast.Delete(targets=[ast.Name(id='a')])] \ + listmiddle() \ + [ast.Delete(targets=[ast.Name(id='d')])]) template3_too_few_nodes = ast.Module( body=[ast.Delete(targets=[ast.Name(id=n)]) for n in 'abcde'] + listmiddle() ) template3_wrong_front = ast.Module( body=[ast.Delete(targets=[ast.Name(id='q')])] + listmiddle() ) template3_wrong_back = ast.Module( body= listmiddle() + [ast.Delete(targets=[ast.Name(id='q')])] ) template3_wrong_node_type = ast.Module(
def subfunc(c=1, d=z): e = 99 print(y) c * 2 a = a*2 a = a - 3 x * 2 x += 1 sum(d + [1]) sysmod.version # modules shouldn't become parameters. """ template = ast.Module(body=[ ast.FunctionDef(name="foobar", args=ast.arguments(args=[ mkarg(name) for name in ['z', 'y', 'a', 'x', 'd'] ]), body=listmiddle() + [ ast.Return(value=ast.Tuple(elts= [ast.Name(id=n, ctx=ast.Load()) for n in ['b', 'subfunc', 'a', 'x']] )) ]), ast.Assign(targets=[ast.Tuple(ctx=ast.Store(), names=[ ast.Name(id=id, ctx=ast.Store()) for id in ['b', 'subfunc', 'a', 'x'] ])], value = ast.Call(func=ast.Name(id="foobar", ctx=ast.Load()), args=[ ast.Name(id=id, ctx=ast.Load()) for id in ['z', 'y', 'a', 'x', 'd'] ]) ) ]) def test_makefunction():