def formatList(node, field):
if type(node) != list:
return None
s = ""
nameMap = { "body" : "line", "targets" : "value", "values" : "value", "orelse" : "line",
"names" : "name", "keys" : "key", "elts" : "value", "ops" : "operator",
"comparators" : "value", "args" : "argument", "keywords" : "keyword" }
# Find what type this is
itemType = nameMap[field] if field in nameMap else "line"
if len(node) > 1:
s = "the " + itemType + "s: "
for line in node:
s += formatNode(line) + ", "
elif len(node) == 1:
s = "the " + itemType + " "
f = formatNode(node[0])
if itemType == "line":
f = "[" + f + "]"
s += f
return s
python类keyword()的实例源码
def channel_history(self, call):
if isinstance(call.func, ast.Attribute):
if call.func.attr == 'logs_from':
if self.interactive and not prompt_change(
'A possible change was found to change logs_from to history.'
):
return call
dest = call.args[0]
call.args = call.args[1:]
if call.args:
limit = call.args[0]
call.keywords.append(ast.keyword(arg='limit', value=limit))
call.args = []
call.func.value = dest
call.func.attr = 'history'
stats_counter['call_changes'] += 1
return call
def stateful_wait_for(self, call):
if isinstance(call.func, ast.Attribute):
if call.func.attr in ['wait_for_message', 'wait_for_reaction']:
if self.interactive and not prompt_change(
'A possible change was found to change {} into wait_for.'.format(call.func.attr)
):
return call
event = call.func.attr.split('_')[2]
event = 'message' if event == 'message' else 'reaction_add'
call.func.attr = 'wait_for'
if call.args:
timeout = call.args[0]
call.args = []
call.keywords.append(ast.keyword(arg='timeout', value=timeout))
call.args.insert(0, ast.Str(s=event))
for kw in list(call.keywords):
if kw.arg != 'check' and kw.arg != 'timeout':
call.keywords.remove(kw)
warnings.warn('wait_for change detected. Rewrite removes the author, channel, and content '
'keyword arguments from this method.')
stats_counter['call_changes'] += 1
return call
def test_classdef(self):
def cls(bases=None, keywords=None, starargs=None, kwargs=None,
body=None, decorator_list=None):
if bases is None:
bases = []
if keywords is None:
keywords = []
if body is None:
body = [ast.Pass()]
if decorator_list is None:
decorator_list = []
return ast.ClassDef("myclass", bases, keywords, starargs,
kwargs, body, decorator_list)
self.stmt(cls(bases=[ast.Name("x", ast.Store())]),
"must have Load context")
self.stmt(cls(keywords=[ast.keyword("x", ast.Name("x", ast.Store()))]),
"must have Load context")
self.stmt(cls(starargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(kwargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(body=[]), "empty body on ClassDef")
self.stmt(cls(body=[None]), "None disallowed")
self.stmt(cls(decorator_list=[ast.Name("x", ast.Store())]),
"must have Load context")
def test_call(self):
func = ast.Name("x", ast.Load())
args = [ast.Name("y", ast.Load())]
keywords = [ast.keyword("w", ast.Name("z", ast.Load()))]
stararg = ast.Name("p", ast.Load())
kwarg = ast.Name("q", ast.Load())
call = ast.Call(ast.Name("x", ast.Store()), args, keywords, stararg,
kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, [None], keywords, stararg, kwarg)
self.expr(call, "None disallowed")
bad_keywords = [ast.keyword("w", ast.Name("z", ast.Store()))]
call = ast.Call(func, args, bad_keywords, stararg, kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, args, keywords, ast.Name("z", ast.Store()), kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, args, keywords, stararg,
ast.Name("w", ast.Store()))
self.expr(call, "must have Load context")
def test_get_keywords(self):
tree = compile_ast('func()')
keywords = fatoptimizer.tools.get_keywords(tree.body[0].value)
self.assertFalse(keywords)
tree = compile_ast('func(x=1, y=2)')
keywords = fatoptimizer.tools.get_keywords(tree.body[0].value)
self.assertEqual(len(keywords), 2)
self.assertIsInstance(keywords[0], ast.keyword)
self.assertEqual(keywords[0].arg, 'x')
self.assertIsInstance(keywords[1], ast.keyword)
self.assertEqual(keywords[1].arg, 'y')
tree = compile_ast('func(arg, *varargs, **kwargs)')
keywords = fatoptimizer.tools.get_keywords(tree.body[0].value)
self.assertEqual(len(keywords), 1)
self.assertIsInstance(keywords[0], ast.keyword)
self.assertIsNone(keywords[0].arg)
tree = compile_ast('func()')
with self.assertRaises(ValueError):
fatoptimizer.tools.get_keywords(tree)
def to_source_any(n):
"""
Convert AST node to string, handling all node types, without fixing comments.
"""
try:
return astor.to_source(n)
except AttributeError:
pass
cls = n.__class__
if cls in astor.misc.all_symbols:
return astor.misc.all_symbols[cls]
def wrap(s):
return '___' + s + '___'
extra_d = {ast.Load: wrap('load'),
ast.Store: wrap('store'),
ast.Del: wrap('del'),
ast.AugLoad: wrap('augload'),
ast.AugStore: wrap('augstore'),
ast.Param: wrap('param'),
ast.keyword: wrap('keyword')}
if cls in extra_d:
return extra_d[cls]
raise AttributeError('unknown node type {}'.format(cls))
def test_classdef(self):
def cls(bases=None, keywords=None, starargs=None, kwargs=None,
body=None, decorator_list=None):
if bases is None:
bases = []
if keywords is None:
keywords = []
if body is None:
body = [ast.Pass()]
if decorator_list is None:
decorator_list = []
return ast.ClassDef("myclass", bases, keywords, starargs,
kwargs, body, decorator_list)
self.stmt(cls(bases=[ast.Name("x", ast.Store())]),
"must have Load context")
self.stmt(cls(keywords=[ast.keyword("x", ast.Name("x", ast.Store()))]),
"must have Load context")
self.stmt(cls(starargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(kwargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(body=[]), "empty body on ClassDef")
self.stmt(cls(body=[None]), "None disallowed")
self.stmt(cls(decorator_list=[ast.Name("x", ast.Store())]),
"must have Load context")
def test_call(self):
func = ast.Name("x", ast.Load())
args = [ast.Name("y", ast.Load())]
keywords = [ast.keyword("w", ast.Name("z", ast.Load()))]
stararg = ast.Name("p", ast.Load())
kwarg = ast.Name("q", ast.Load())
call = ast.Call(ast.Name("x", ast.Store()), args, keywords, stararg,
kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, [None], keywords, stararg, kwarg)
self.expr(call, "None disallowed")
bad_keywords = [ast.keyword("w", ast.Name("z", ast.Store()))]
call = ast.Call(func, args, bad_keywords, stararg, kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, args, keywords, ast.Name("z", ast.Store()), kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, args, keywords, stararg,
ast.Name("w", ast.Store()))
self.expr(call, "must have Load context")
def make_jinja_call(render_function_name, used_variables, string_value, ctx):
used_defined_variables = ctx & used_variables
undefined_variables = used_variables - ctx
if len(undefined_variables) > 0:
translation_error("Undefined variables: " + ", ".join(undefined_variables), string_value)
used_parsers = used_defined_variables & set(FavaCode.get_known_parsers().keys())
used_user_variables = used_defined_variables - used_parsers
keyword_arguments = [keyword(arg=key, value=Name(id=key, ctx=Load()))
for key in used_user_variables] + \
[keyword(arg=key,
value=_build_subscript(Name(id='shared', ctx=Load()),
Name(id=key, ctx=Load())))
for key in used_parsers]
return FavaCode(
Call(func=Name(id=render_function_name, ctx=Load()),
args=[Str(s=string_value)],
keywords=keyword_arguments,
starargs=None, kwargs=None),
[],
parsers=used_parsers)
def test_classdef(self):
def cls(bases=None, keywords=None, starargs=None, kwargs=None,
body=None, decorator_list=None):
if bases is None:
bases = []
if keywords is None:
keywords = []
if body is None:
body = [ast.Pass()]
if decorator_list is None:
decorator_list = []
return ast.ClassDef("myclass", bases, keywords, starargs,
kwargs, body, decorator_list)
self.stmt(cls(bases=[ast.Name("x", ast.Store())]),
"must have Load context")
self.stmt(cls(keywords=[ast.keyword("x", ast.Name("x", ast.Store()))]),
"must have Load context")
self.stmt(cls(starargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(kwargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(body=[]), "empty body on ClassDef")
self.stmt(cls(body=[None]), "None disallowed")
self.stmt(cls(decorator_list=[ast.Name("x", ast.Store())]),
"must have Load context")
def test_call(self):
func = ast.Name("x", ast.Load())
args = [ast.Name("y", ast.Load())]
keywords = [ast.keyword("w", ast.Name("z", ast.Load()))]
stararg = ast.Name("p", ast.Load())
kwarg = ast.Name("q", ast.Load())
call = ast.Call(ast.Name("x", ast.Store()), args, keywords, stararg,
kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, [None], keywords, stararg, kwarg)
self.expr(call, "None disallowed")
bad_keywords = [ast.keyword("w", ast.Name("z", ast.Store()))]
call = ast.Call(func, args, bad_keywords, stararg, kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, args, keywords, ast.Name("z", ast.Store()), kwarg)
self.expr(call, "must have Load context")
call = ast.Call(func, args, keywords, stararg,
ast.Name("w", ast.Store()))
self.expr(call, "must have Load context")
def ast(self):
"""Create Python AST of this predicate.
:return: AST representation of predicate
"""
# we could directly use db[task] in predicates, but predicates should not handle database errors,
# so leave them on higher level (selinon) and index database before predicate is being called
kwargs = []
# we want to avoid querying to database if possible, if a predicate does not require message, do not ask for it
if self.requires_message():
# this can raise an exception if check was not run, since we are accessing storage that can be None
kwargs.append(ast.keyword(arg='message',
value=ast.Call(func=ast.Attribute(value=ast.Name(id='db', ctx=ast.Load()),
attr='get', ctx=ast.Load()),
args=[ast.Str(s=self._task_str_name())],
keywords=[], starargs=None, kwargs=None)))
if self.requires_node_args():
kwargs.append(ast.keyword(arg='node_args', value=ast.Name(id='node_args', ctx=ast.Load())))
kwargs.extend([ast.keyword(arg=k, value=ast.Str(s=v)) for k, v in self._args.items()])
return ast.Call(func=ast.Name(id=self._func.__name__, ctx=ast.Load()),
args=[], starargs=None, kwargs=None, keywords=kwargs)
def ast(self):
"""Create Python AST of this predicate.
:return: AST representation of predicate
"""
# we could directly use db[task] in predicates, but predicates should not handle database errors,
# so leave them on higher level (selinon) and index database before predicate is being called
kwargs = []
# we want to avoid querying to database if possible, if a predicate does not require message, do not ask for it
if self.requires_message():
# this can raise an exception if check was not run, since we are accessing storage that can be None
kwargs.append(ast.keyword(arg='message',
value=ast.Call(func=ast.Attribute(value=ast.Name(id='db', ctx=ast.Load()),
attr='get', ctx=ast.Load()),
args=[ast.Str(s=self._task_str_name())],
keywords=[], starargs=None, kwargs=None)))
if self.requires_node_args():
kwargs.append(ast.keyword(arg='node_args', value=ast.Name(id='node_args', ctx=ast.Load())))
kwargs.extend([ast.keyword(arg=k, value=ast.Str(s=v)) for k, v in self._args.items()])
return ast.Call(func=ast.Name(id=self._func.__name__, ctx=ast.Load()),
args=[], starargs=None, kwargs=None, keywords=kwargs)
def get_sink_args(cfg_node):
if type(cfg_node) == AssignmentNode:
return get_sink_args(cfg_node.ast_node.value)
elif type(cfg_node) == ReturnNode:
return get_sink_args(cfg_node.ast_node.value)
elif isinstance(cfg_node, Node):
return get_sink_args(cfg_node.ast_node)
elif isinstance(cfg_node, ast.Call):
args = list()
for arg in cfg_node.args + cfg_node.keywords:
if isinstance(arg, ast.Name):
args.append(arg.id)
elif isinstance(arg, ast.Str):
args.append(arg.s)
elif isinstance(arg, ast.Call):
args.extend(get_sink_args(arg))
elif isinstance(arg, ast.keyword):
args.append(arg.value)
elif isinstance(arg, ast.Attribute):
import ast_helper
args.append(ast_helper.get_call_names_as_string(arg))
else:
raise Exception('Unexpected argument type:', type(arg))
return args
elif isinstance(cfg_node, ast.Str):
return None
else:
raise Exception('Unexpected node type:', type(cfg_node))
def visit_Call_35(self, call):
"""
visit `ast.Call` nodes on Python3.5 and after
"""
new_func, func_expl = self.visit(call.func)
arg_expls = []
new_args = []
new_kwargs = []
for arg in call.args:
res, expl = self.visit(arg)
arg_expls.append(expl)
new_args.append(res)
for keyword in call.keywords:
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
if keyword.arg:
arg_expls.append(keyword.arg + "=" + expl)
else: ## **args have `arg` keywords with an .arg of None
arg_expls.append("**" + expl)
expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_expl
def visit_Call_legacy(self, call):
"""
visit `ast.Call nodes on 3.4 and below`
"""
new_func, func_expl = self.visit(call.func)
arg_expls = []
new_args = []
new_kwargs = []
new_star = new_kwarg = None
for arg in call.args:
res, expl = self.visit(arg)
new_args.append(res)
arg_expls.append(expl)
for keyword in call.keywords:
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
arg_expls.append(keyword.arg + "=" + expl)
if call.starargs:
new_star, expl = self.visit(call.starargs)
arg_expls.append("*" + expl)
if call.kwargs:
new_kwarg, expl = self.visit(call.kwargs)
arg_expls.append("**" + expl)
expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs,
new_star, new_kwarg)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_expl
# ast.Call signature changed on 3.5,
# conditionally change which methods is named
# visit_Call depending on Python version
def visit_Print(self, node):
if node.__class__ != ast.Print:
return node
dummy_func = ast.Name(id="print", ctx=ast.Load())
keywords = []
if not node.nl:
end = ast.keyword(arg="end", value=ast.Str(s=""))
keywords.append(end)
dummy_call = ast.Call(func=dummy_func, args=node.values, keywords=keywords, starargs=None, kwargs=None)
return ast.Expr(value=dummy_call)
def stateful_send_file(self, call):
if isinstance(call.func, ast.Attribute):
if call.func.attr == 'send_file':
if self.interactive and not prompt_change(
'A possible change was found to change send_file to send.'
):
return call
dest = call.args[0]
send_as = call.args[1]
content = None
filename = None
for kw in list(call.keywords):
if kw.arg == 'filename':
filename = kw
if kw.arg == 'content':
content = kw
if filename is None:
filename = ast.keyword(arg='filename', value=send_as)
call.func.value = dest
call.func.attr = 'send'
call.args = []
if content:
call.args.append(content.value)
call.keywords = []
file_kw = ast.keyword()
file_kw.arg = 'file'
discord_file_call = ast.Call()
discord_file_call.func = ast.Attribute(value=ast.Name(id='discord', ctx=ast.Load()), attr='File',
ctx=ast.Load())
discord_file_call.args = [send_as, filename.value]
discord_file_call.keywords = []
file_kw.value = discord_file_call
call.keywords.append(file_kw)
stats_counter['call_changes'] += 1
return call
def stateful_change_nickname(self, call):
if isinstance(call.func, ast.Attribute):
if call.func.attr == 'change_nickname':
if self.interactive and not prompt_change(
'A possible change was found to make change_nickname into edit.'
):
return call
member = call.args[0]
call.func.value = member
call.func.attr = 'edit'
nick = call.args[1]
call.args = []
call.keywords = [ast.keyword(arg='nick', value=nick)]
stats_counter['call_changes'] += 1
return call
def stateful_edit_message(self, call):
if isinstance(call.func, ast.Attribute):
if call.func.attr == 'edit_message':
if self.interactive and not prompt_change(
'A possible change was found to make {} stateful.'.format(call.func.attr)
):
return call
call.func.attr = 'edit'
message = call.args[0]
call.func.value = message
content = call.args[1]
call.args = call.args[2:]
call.keywords.append(ast.keyword(arg='content', value=content))
stats_counter['call_changes'] += 1
return call
def auto_symbol(tokens, local_dict, global_dict):
"""Inserts calls to ``Symbol`` for undefined variables."""
result = []
prevTok = (None, None)
tokens.append((None, None)) # so zip traverses all tokens
for tok, nextTok in zip(tokens, tokens[1:]):
tokNum, tokVal = tok
nextTokNum, nextTokVal = nextTok
if tokNum == NAME:
name = tokVal
if (name in ['True', 'False', 'None']
or iskeyword(name)
or name in local_dict
# Don't convert attribute access
or (prevTok[0] == OP and prevTok[1] == '.')
# Don't convert keyword arguments
or (prevTok[0] == OP and prevTok[1] in ('(', ',')
and nextTokNum == OP and nextTokVal == '=')):
result.append((NAME, name))
continue
elif name in global_dict:
obj = global_dict[name]
if isinstance(obj, (Basic, type)) or callable(obj):
result.append((NAME, name))
continue
result.extend([
(NAME, 'Symbol'),
(OP, '('),
(NAME, repr(str(name))),
(OP, ')'),
])
else:
result.append((tokNum, tokVal))
prevTok = (tokNum, tokVal)
return result
def visit_BinOp(self, node):
if node.op.__class__ in self.operators:
sympy_class = self.operators[node.op.__class__]
right = self.visit(node.right)
if isinstance(node.op, ast.Sub):
right = ast.UnaryOp(op=ast.USub(), operand=right)
elif isinstance(node.op, ast.Div):
right = ast.Call(
func=ast.Name(id='Pow', ctx=ast.Load()),
args=[right, ast.UnaryOp(op=ast.USub(), operand=ast.Num(1))],
keywords=[ast.keyword(arg='evaluate', value=ast.Name(id='False', ctx=ast.Load()))],
starargs=None,
kwargs=None
)
new_node = ast.Call(
func=ast.Name(id=sympy_class, ctx=ast.Load()),
args=[self.visit(node.left), right],
keywords=[ast.keyword(arg='evaluate', value=ast.Name(id='False', ctx=ast.Load()))],
starargs=None,
kwargs=None
)
if sympy_class in ('Add', 'Mul'):
# Denest Add or Mul as appropriate
new_node.args = self.flatten(new_node.args, sympy_class)
return new_node
return node
def _is_star_star_kwarg(node):
return isinstance(node, ast.keyword) and node.arg is None
def make_keywords(self, kwarg_nodes):
if kwarg_nodes is None:
kwarg_nodes = {}
assert isinstance(kwarg_nodes, dict)
return [ast.keyword(arg=name, value=value)
for name, value in kwarg_nodes.items()]
def visit_Call_35(self, call):
"""
visit `ast.Call` nodes on Python3.5 and after
"""
new_func, func_expl = self.visit(call.func)
arg_expls = []
new_args = []
new_kwargs = []
for arg in call.args:
res, expl = self.visit(arg)
arg_expls.append(expl)
new_args.append(res)
for keyword in call.keywords:
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
if keyword.arg:
arg_expls.append(keyword.arg + "=" + expl)
else: ## **args have `arg` keywords with an .arg of None
arg_expls.append("**" + expl)
expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_expl
def visit_Call_legacy(self, call):
"""
visit `ast.Call nodes on 3.4 and below`
"""
new_func, func_expl = self.visit(call.func)
arg_expls = []
new_args = []
new_kwargs = []
new_star = new_kwarg = None
for arg in call.args:
res, expl = self.visit(arg)
new_args.append(res)
arg_expls.append(expl)
for keyword in call.keywords:
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
arg_expls.append(keyword.arg + "=" + expl)
if call.starargs:
new_star, expl = self.visit(call.starargs)
arg_expls.append("*" + expl)
if call.kwargs:
new_kwarg, expl = self.visit(call.kwargs)
arg_expls.append("**" + expl)
expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs,
new_star, new_kwarg)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_expl
# ast.Call signature changed on 3.5,
# conditionally change which methods is named
# visit_Call depending on Python version
def test_iter_child_nodes(self):
node = ast.parse("spam(23, 42, eggs='leek')", mode='eval')
self.assertEqual(len(list(ast.iter_child_nodes(node.body))), 4)
iterator = ast.iter_child_nodes(node.body)
self.assertEqual(next(iterator).id, 'spam')
self.assertEqual(next(iterator).n, 23)
self.assertEqual(next(iterator).n, 42)
self.assertEqual(ast.dump(next(iterator)),
"keyword(arg='eggs', value=Str(s='leek'))"
)
def visit_Call_35(self, call):
"""
visit `ast.Call` nodes on Python3.5 and after
"""
new_func, func_expl = self.visit(call.func)
arg_expls = []
new_args = []
new_kwargs = []
for arg in call.args:
res, expl = self.visit(arg)
arg_expls.append(expl)
new_args.append(res)
for keyword in call.keywords:
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
if keyword.arg:
arg_expls.append(keyword.arg + "=" + expl)
else: ## **args have `arg` keywords with an .arg of None
arg_expls.append("**" + expl)
expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_expl
def visit_Call_legacy(self, call):
"""
visit `ast.Call nodes on 3.4 and below`
"""
new_func, func_expl = self.visit(call.func)
arg_expls = []
new_args = []
new_kwargs = []
new_star = new_kwarg = None
for arg in call.args:
res, expl = self.visit(arg)
new_args.append(res)
arg_expls.append(expl)
for keyword in call.keywords:
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
arg_expls.append(keyword.arg + "=" + expl)
if call.starargs:
new_star, expl = self.visit(call.starargs)
arg_expls.append("*" + expl)
if call.kwargs:
new_kwarg, expl = self.visit(call.kwargs)
arg_expls.append("**" + expl)
expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs,
new_star, new_kwarg)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_expl
# ast.Call signature changed on 3.5,
# conditionally change which methods is named
# visit_Call depending on Python version