def _extract_scripts_from_project(setup_filename='setup.py'):
"""Parse setup.py and return scripts"""
if not os.path.isfile(setup_filename):
return ''
mock_setup = textwrap.dedent('''\
def setup(*args, **kwargs):
__setup_calls__.append((args, kwargs))
''')
parsed_mock_setup = ast.parse(mock_setup, filename=setup_filename)
with open(setup_filename, 'rt') as setup_file:
parsed = ast.parse(setup_file.read())
for index, node in enumerate(parsed.body[:]):
if (not isinstance(node, ast.Expr) or
not isinstance(node.value, ast.Call) or
node.value.func.id != 'setup'):
continue
parsed.body[index:index] = parsed_mock_setup.body
break
fixed = ast.fix_missing_locations(parsed)
codeobj = compile(fixed, setup_filename, 'exec')
local_vars = {}
global_vars = {'__setup_calls__': []}
exec(codeobj, global_vars, local_vars)
_, kwargs = global_vars['__setup_calls__'][0]
return ','.join([os.path.basename(f) for f in kwargs.get('scripts', [])])
python类Call()的实例源码
def __init__(self, stmt, context):
self.stmt = stmt
self.context = context
self.stmt_table = {
ast.Expr: self.expr,
ast.Pass: self.parse_pass,
ast.AnnAssign: self.ann_assign,
ast.Assign: self.assign,
ast.If: self.parse_if,
ast.Call: self.call,
ast.Assert: self.parse_assert,
ast.For: self.parse_for,
ast.AugAssign: self.aug_assign,
ast.Break: self.parse_break,
ast.Return: self.parse_return,
}
stmt_type = self.stmt.__class__
if stmt_type in self.stmt_table:
self.lll_node = self.stmt_table[stmt_type]()
elif isinstance(stmt, ast.Name) and stmt.id == "throw":
self.lll_node = LLLnode.from_list(['assert', 0], typ=None, pos=getpos(stmt))
else:
raise StructureException("Unsupported statement type", stmt)
def assign_tuple_target(self, node, right_hand_side_variables):
new_assignment_nodes = list()
for i, target in enumerate(node.targets[0].elts):
value = node.value.elts[i]
label = LabelVisitor()
label.visit(target)
if isinstance(value, ast.Call):
new_ast_node = ast.Assign(target, value)
new_ast_node.lineno = node.lineno
new_assignment_nodes.append( self.assignment_call_node(label.result, new_ast_node))
else:
label.result += ' = '
label.visit(value)
new_assignment_nodes.append(self.append_node(AssignmentNode(label.result, self.extract_left_hand_side(target), ast.Assign(target, value), right_hand_side_variables, line_number = node.lineno, path=self.filenames[-1])))
self.connect_nodes(new_assignment_nodes)
return ControlFlowNode(new_assignment_nodes[0], [new_assignment_nodes[-1]], []) # return the last added node
def visit_Assign(self, node):
rhs_visitor = RHSVisitor()
rhs_visitor.visit(node.value)
if isinstance(node.targets[0], ast.Tuple): # x,y = [1,2]
if isinstance(node.value, ast.Tuple):
return self.assign_tuple_target(node, rhs_visitor.result)
elif isinstance(node.value, ast.Call):
call = None
for element in node.targets[0].elts:
label = LabelVisitor()
label.visit(element)
call = self.assignment_call_node(label.result, node)
return call
elif len(node.targets) > 1: # x = y = 3
return self.assign_multi_target(node, rhs_visitor.result)
else:
if isinstance(node.value, ast.Call): # x = call()
label = LabelVisitor()
label.visit(node.targets[0])
return self.assignment_call_node(label.result, node)
else: # x = 4
label = LabelVisitor()
label.visit(node)
return self.append_node(AssignmentNode(label.result, self.extract_left_hand_side(node.targets[0]), node, rhs_visitor.result, line_number = node.lineno, path=self.filenames[-1]))
def visit_For(self, node):
self.undecided = True # Used for handling functions in for loops
#issue23
iterator_label = LabelVisitor()
iterator = iterator_label.visit(node.iter)
self.undecided = False
target_label = LabelVisitor()
target = target_label.visit(node.target)
for_node = self.append_node(Node("for " + target_label.result + " in " + iterator_label.result + ':', node, line_number = node.lineno, path=self.filenames[-1]))
if isinstance(node.iter, ast.Call) and get_call_names_as_string(node.iter.func) in self.function_names:
last_node = self.visit(node.iter)
last_node.connect(for_node)
return self.loop_node_skeleton(for_node, node)
def add_function(self, call_node, definition):
try:
self.function_index += 1
def_node = definition.node
saved_variables = self.save_local_scope(def_node.lineno)
parameters = self.save_actual_parameters_in_temp(call_node.args, Arguments(def_node.args), call_node.lineno)
self.filenames.append(definition.path)
self.create_local_scope_from_actual_parameters(call_node.args, Arguments(def_node.args), def_node.lineno)
function_nodes = self.get_function_nodes(definition)
self.filenames.pop() # Maybe move after restore nodes
restore_nodes = self.restore_saved_local_scope(saved_variables, parameters, def_node.lineno)
self.return_handler(call_node, function_nodes, restore_nodes)
self.function_return_stack.pop()
except IndexError:
error_call = get_call_names_as_string(call_node.func)
print('Error: Possible nameclash in "{}". Call omitted!\n'.format(error_call))
return self.nodes[-1]
def get_decorators(cls):
decorators = {}
def visit_FunctionDef(node):
decorators[node.name] = []
for n in node.decorator_list:
name = ''
if isinstance(n, ast.Call):
name = n.func.attr if isinstance(n.func, ast.Attribute) else n.func.id
else:
name = n.attr if isinstance(n, ast.Attribute) else n.id
args = [a.s for a in n.args] if hasattr(n, 'args') else []
decorators[node.name].append((name, args))
node_iter = ast.NodeVisitor()
node_iter.visit_FunctionDef = visit_FunctionDef
_cls = cls if inspect.isclass(cls) else cls.__class__
node_iter.visit(ast.parse(inspect.getsource(_cls)))
return decorators
def search(func, depth=1):
local_vars = sys._getframe(depth).f_locals
source = get_source_code(func)
tree = ast.parse(source)
child_funcs = []
for node in ast.walk(tree):
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Name):
child_funcs.append(node.func.id)
elif (isinstance(node, ast.Name) and node.id in local_vars and callable(local_vars[node.id]) and node.id not in sys.builtin_module_names):
child_funcs.append(node.id)
child_load_str = ''
for child in child_funcs:
if child in local_vars:
try:
load_string = search(local_vars[child], depth=(depth + 1))
child_load_str += load_string + '\n'
except Exception as e:
pass
load_str = child_load_str + source
return load_str
def replaceHazards(a):
if not isinstance(a, ast.AST):
return
for field in ast.walk(a):
if type(a) == ast.Import:
for i in range(len(a.names)):
if a.names[i].name not in supportedLibraries:
if not (a.names[i].name[0] == "r" and a.names[i].name[1] in "0123456789") and not ("NotAllowed" in a.names[i].name):
a.names[i].name = a.names[i].name + "NotAllowed"
elif type(a) == ast.ImportFrom:
if a.module not in supportedLibraries:
if not (a.module[0] == "r" and a.module[1] in "0123456789") and not ("NotAllowed" in a.module):
a.module = a.module + "NotAllowed"
elif type(a) == ast.Call:
if type(a.func) == ast.Name and a.func.id in ["compile", "eval", "execfile", "file", "open", "__import__", "apply"]:
a.func.id = a.func.id + "NotAllowed"
def findHelperFunction(a, helperId, helperCount):
"""Finds the first helper function used in the ast"""
if not isinstance(a, ast.AST):
return None
# Check all the children, so that we don't end up with a recursive problem
for child in ast.iter_child_nodes(a):
f = findHelperFunction(child, helperId, helperCount)
if f != None:
return f
# Then check if this is the right call
if type(a) == ast.Call:
if type(a.func) == ast.Name and a.func.id == helperId:
if helperCount[0] > 0:
helperCount[0] -= 1
else:
return a
return None
def cleanupRanges(a):
"""Remove any range shenanigans, because Python lets you include unneccessary values"""
if not isinstance(a, ast.AST):
return a
if type(a) == ast.Call:
if type(a.func) == ast.Name:
if a.func.id in ["range"]:
if len(a.args) == 3:
# The step defaults to 1!
if type(a.args[2]) == ast.Num and a.args[2].n == 1:
a.args = a.args[:-1]
if len(a.args) == 2:
# The start defaults to 0!
if type(a.args[0]) == ast.Num and a.args[0].n == 0:
a.args = a.args[1:]
return applyToChildren(a, cleanupRanges)
def cleanupSlices(a):
"""Remove any slice shenanigans, because Python lets you include unneccessary values"""
if not isinstance(a, ast.AST):
return a
if type(a) == ast.Subscript:
if type(a.slice) == ast.Slice:
# Lower defaults to 0
if a.slice.lower != None and type(a.slice.lower) == ast.Num and a.slice.lower.n == 0:
a.slice.lower = None
# Upper defaults to len(value)
if a.slice.upper != None and type(a.slice.upper) == ast.Call and \
type(a.slice.upper.func) == ast.Name and a.slice.upper.func.id == "len":
if compareASTs(a.value, a.slice.upper.args[0], checkEquality=True) == 0:
a.slice.upper = None
# Step defaults to 1
if a.slice.step != None and type(a.slice.step) == ast.Num and a.slice.step.n == 1:
a.slice.step = None
return applyToChildren(a, cleanupSlices)
def process(fl, external, genfiles, vendor):
src = open(fl).read()
tree = ast.parse(src, fl)
lst = []
wksp = WORKSPACE(external, genfiles, vendor)
for stmt in ast.walk(tree):
stmttype = type(stmt)
if stmttype == ast.Call:
fn = getattr(wksp, stmt.func.id, "")
if not callable(fn):
continue
path, name = keywords(stmt)
if path.endswith(".git"):
path = path[:-4]
path = pathmap.get(path, path)
tup = fn(name, path)
lst.append(tup)
return lst
def return_handler(self, call_node, function_nodes, saved_function_call_index, first_node):
"""Handle the return from a function during a function call.
Args:
call_node(ast.Call) : The node that calls the definition.
function_nodes(list[Node]): List of nodes of the function being called.
saved_function_call_index(int): Unique number for each call.
first_node(EntryOrExitNode or RestoreNode): Used to connect previous statements to this function.
"""
for node in function_nodes:
# Only `Return`s and `Raise`s can be of type ConnectToExitNode
if isinstance(node, ConnectToExitNode):
# Create e.g. ¤call_1 = ret_func_foo RestoreNode
LHS = CALL_IDENTIFIER + 'call_' + str(saved_function_call_index)
RHS = 'ret_' + get_call_names_as_string(call_node.func)
return_node = RestoreNode(LHS + ' = ' + RHS,
LHS,
[RHS],
line_number=call_node.lineno,
path=self.filenames[-1])
return_node.first_node = first_node
self.nodes[-1].connect(return_node)
self.nodes.append(return_node)
return
def assign_tuple_target(self, node, right_hand_side_variables):
new_assignment_nodes = list()
for i, target in enumerate(node.targets[0].elts):
value = node.value.elts[i]
label = LabelVisitor()
label.visit(target)
if isinstance(value, ast.Call):
new_ast_node = ast.Assign(target, value)
new_ast_node.lineno = node.lineno
new_assignment_nodes.append(self.assignment_call_node(label.result, new_ast_node))
else:
label.result += ' = '
label.visit(value)
new_assignment_nodes.append(self.append_node(AssignmentNode(label.result, self.extract_left_hand_side(target), ast.Assign(target, value), right_hand_side_variables, line_number=node.lineno, path=self.filenames[-1])))
self.connect_nodes(new_assignment_nodes)
return ControlFlowNode(new_assignment_nodes[0], [new_assignment_nodes[-1]], []) # return the last added node
def visit_Call(self, node):
# This will not visit Flask in Flask(__name__) but it will visit request in `request.args.get()
if not isinstance(node.func, ast.Name):
self.visit(node.func)
for arg in itertools.chain(node.args, node.keywords):
if isinstance(arg, ast.Call):
if isinstance(arg.func, ast.Name):
# We can't just visit because we need to add 'ret_'
self.result.append('ret_' + arg.func.id)
elif isinstance(arg.func, ast.Attribute):
# e.g. html.replace('{{ param }}', param)
# func.attr is replace
# func.value.id is html
# We want replace
self.result.append('ret_' + arg.func.attr)
else:
# Deal with it when we have code that triggers it.
raise
else:
self.visit(arg)
def get_sink_args(cfg_node):
if isinstance(cfg_node.ast_node, ast.Call):
rhs_visitor = RHSVisitor()
rhs_visitor.visit(cfg_node.ast_node)
return rhs_visitor.result
elif isinstance(cfg_node.ast_node, ast.Assign):
return cfg_node.right_hand_side_variables
vv = VarsVisitor()
other_results = list()
if isinstance(cfg_node, BBorBInode):
other_results = cfg_node.args
else:
vv.visit(cfg_node.ast_node)
return vv.result + other_results
def get_call_names_helper(node, result):
"""Recursively finds all function names."""
if isinstance(node, ast.Name):
if node.id not in BLACK_LISTED_CALL_NAMES:
result.append(node.id)
return result
elif isinstance(node, ast.Call):
return result
elif isinstance(node, ast.Subscript):
return get_call_names_helper(node.value, result)
elif isinstance(node, ast.Str):
result.append(node.s)
return result
else:
result.append(node.attr)
return get_call_names_helper(node.value, result)
def test_call_arg_kwarg(self):
matches1 = list(self.m.match('call[kwarg=a]',
self.filepath('calls.py')))
matches2 = list(self.m.match('call[kwarg=x]',
self.filepath('calls.py')))
matches3 = list(self.m.match('call[arg=bar]',
self.filepath('calls.py')))
matches4 = list(self.m.match('[arg=bang]', self.filepath('calls.py')))
self.assertEqual(len(matches1), 1)
self.assertEqual(len(matches2), 2)
self.assertEqual(len(matches3), 2)
self.assertEqual(len(matches4), 2)
self.assertIsInstance(matches1[0][0], ast.Call)
self.assertIsInstance(matches2[0][0], ast.Call)
self.assertIsInstance(matches2[1][0], ast.Call)
self.assertIsInstance(matches3[0][0], ast.Call)
self.assertIsInstance(matches3[1][0], ast.Call)
self.assertIsInstance(matches4[0][0], ast.Call)
def match_type(self, typ, node):
if typ == 'class':
return isinstance(node, ast.ClassDef)
if typ == 'def':
return isinstance(node, ast.FunctionDef)
if typ == 'import':
return isinstance(node, (ast.Import, ast.ImportFrom))
if typ == 'assign':
return isinstance(node, ast.Assign)
if typ == 'attr':
return isinstance(node, ast.Attribute)
if typ == 'call':
if isinstance(node, ast.Call):
return True
# Python 2.x compatibility
return hasattr(ast, 'Print') and isinstance(node, ast.Print)
def stateful_get_all_emojis(self, expr):
if not isinstance(expr.value, ast.Await):
return expr
if not isinstance(expr.value.value, ast.Call):
return expr
call = expr.value.value
if isinstance(call.func, ast.Attribute):
if call.func.attr == 'get_all_emojis':
if self.interactive and not prompt_change(
'A possible change was found to make get_all_emojis stateful.'
):
return expr
new_expr = ast.Expr()
new_expr.value = ast.Attribute()
new_expr.value.value = call.func.value
new_expr.value.attr = 'emojis'
new_expr.value.ctx = ast.Load()
new_expr = ast.copy_location(new_expr, expr)
stats_counter['expr_changes'] += 1
return new_expr
return expr
def _is_chalice_view(self, node):
# type: (ast.FunctionDef) -> bool
# We can certainly improve on this, but this check is more
# of a heuristic for the time being. The ideal way to do this
# is to infer the Chalice type and ensure the function is
# decorated with the Chalice type's route() method.
decorator_list = node.decorator_list
if not decorator_list:
return False
for decorator in decorator_list:
if isinstance(decorator, ast.Call) and \
isinstance(decorator.func, ast.Attribute):
if decorator.func.attr == 'route' and \
decorator.args:
return True
# For lambda_function and schedule decorator.args
# not present.
if decorator.func.attr in ('lambda_function', 'schedule'):
return True
return False
def get_node_name_with_extra_code(self, node, extra_code):
_ext_info = {'extra_code': extra_code}
if isinstance(node, ast.Call):
left_name = self.temp_variable.get_new_name()
_temp = '%s\n' % self.dispatch(node, _ext_info)
extra_code = '%s%s%s=$__return_%s\n' % (
_ext_info['extra_code'],
_temp,
left_name,
node.func.id,
)
left_name = '$%s' % left_name
else:
left_name = self.dispatch(node, _ext_info)
extra_code = _ext_info['extra_code']
return left_name, extra_code
def generate_assign(self, node, ext_info):
target_code = ''
if isinstance(node.targets[0], ast.Name):
target_code = self.dispatch(node.targets[0])
else:
raise CompileError()
if isinstance(node.value, ast.Call):
return '%s\n%s=$__return_%s' % (
self.dispatch(node.value),
target_code,
node.value.func.id
)
else:
ext_info = {}
value_code = self.dispatch(node.value, ext_info)
extra_code = ext_info.get('extra_code', '')
if value_code is None:
raise CompileError()
return extra_code + target_code + '=' + value_code
def generate_call(self, node, ext_info={}):
if hasattr(node, 'kwargs'):
if not node.kwargs is None:
raise SyntaxNotSupportError('Keyword arguments is not support yet.')
elif not len(node.keywords) == 0:
raise SyntaxNotSupportError('Keyword arguments is not support yet.')
function_name = node.func.id
if len(node.args) is 0:
return '%s' % function_name
argument_list = []
if is_system_function(function_name):
return self.generate_system_function(node, ext_info)
for x in node.args:
if isinstance(x, ast.Call):
new_temp_variable = self.temp_variable.get_new_name()
self.code_buffer.append(self.dispatch(x))
self.code_buffer.append('%s=$__return_%s' % (new_temp_variable, x.func.id))
argument_list.append(new_temp_variable)
else:
ext_info['is_arg'] = True
argument_list.append(self.dispatch(x, ext_info=ext_info))
arguments_code = ' '.join(argument_list)
return '%s %s' % (function_name, arguments_code)
def get_type(self, node):
if isinstance(node, ast.Num):
return Type.NUMBER
elif isinstance(node, ast.Str):
return Type.STRING
elif isinstance(node, ast.Name):
if self.variables[node.id] is not None:
return self.variables[node.id].var_type
else:
return Type.VOID
elif isinstance(node, ast.BinOp):
if self.get_type(node.left).is_number and self.get_type(node.right).is_number:
return Type.NUMBER
elif self.get_type(node.left).is_string or self.get_type(node.right).is_string:
return Type.STRING
elif isinstance(node, ast.Call):
return self.functions[node.func.id].return_type
else:
return Type.VOID
def requests_auth_literal(context):
if re.match(r'requests\.(get|head|post|put|)', context.call_function_name_qual) is None:
return
call_node = context.node
kwarg_nodes = dict((kwarg.arg, kwarg.value) for kwarg in call_node.keywords)
if 'auth' not in kwarg_nodes:
return
auth_value = context.call_keywords.get('auth')
if auth_value is not None:
return bandit.Issue(
severity=bandit.HIGH,
confidence=(bandit.HIGH if (isinstance(auth_value, (list, tuple)) and len(auth_value) == 2) else bandit.MEDIUM),
text="Hard-coded credentials are being passed to the requests library for basic authentication."
)
if not isinstance(kwarg_nodes['auth'], ast.Call):
return
arg_call = b_utils.get_call_name(kwarg_nodes['auth'], context._context['import_aliases'])
if arg_call not in ('requests.HTTPBasicAuth', 'requests.HTTPDigestAuth'):
return
parent = s_utils.get_top_parent_node(call_node)
username = next(s_utils.get_call_arg_values(parent, kwarg_nodes['auth'], arg=0, child=call_node), None)
password = next(s_utils.get_call_arg_values(parent, kwarg_nodes['auth'], arg=1, kwarg='password', child=call_node), None)
return s_utils.report_hardcoded_credentials('requests', username, password)
def get_attribute_name(node, import_aliases=None):
import_aliases = import_aliases or {}
if not isinstance(node, ast.Attribute):
raise ValueError('node must be an instance of ast.Attribute')
base = node.attr
name = ''
node = node.value
while isinstance(node, ast.Attribute):
name = node.attr + '.' + name
node = node.value
if isinstance(node, (ast.Call, ast.Subscript)):
return None
if not isinstance(node, ast.Name):
raise ValueError('could not resolve node for attribute')
name = (node.id + '.' + name)[:-1]
return import_aliases.get(name, name) + '.' + base
def get_call_arg_values(parent, call_node, arg=None, kwarg=None, child=None):
"""Only returns literals."""
if not isinstance(call_node, ast.Call):
raise ValueError('call_node must be an ast.Call instance')
if arg is None and kwarg is None:
raise RuntimeError('either an arg or kwarg must be specified')
arg_node = None
if arg is not None:
if not isinstance(arg, int):
raise ValueError('arg must be specified as a 0-indexed argument position')
if arg < len(call_node.args):
arg_node = call_node.args[arg]
if arg_node is None and kwarg is not None:
if not isinstance(kwarg, str):
raise ValueError('kwarg must be specified as the string of a keyword argument name')
arg_node = next((kw.value for kw in call_node.keywords if kw.arg == kwarg), None)
if arg_node is None:
return
if not hasattr(arg_node, 'parent'):
arg_node.parent = call_node
for arg_value in iter_expr_literal_values(parent, arg_node, child=child):
yield arg_value
def method_could_be_class(node, context, search_classes):
if isinstance(node, ast.Call):
call_node = node
parent = get_top_parent_node(call_node)
klass_found = next(
(klass for klass in iter_method_classes(parent, call_node, context) if klass in search_classes),
None
)
return klass_found is not None
elif isinstance(node, ast.FunctionDef):
klass_node = node.parent
if not isinstance(klass_node, ast.ClassDef):
# not sure what happened here
return False
for base_klass in klass_node.bases:
base_klass = base_klass.id
if base_klass in search_classes:
return True
if name_is_imported(base_klass, context, search_classes):
return True
else:
raise ValueError('node must be either an ast.Call or ast.FunctionDef instance')