def _remove_statements(self, stmt): """ This is the part where statements are being stripped. Due to lazy evaluation, statements like a = func; b = a; b() have to be evaluated. """ evaluator = self._evaluator types = [] # Remove the statement docstr stuff for now, that has to be # implemented with the evaluator class. #if stmt.docstr: #res_new.append(stmt) check_instance = None if isinstance(stmt, er.InstanceElement) and stmt.is_class_var: check_instance = stmt.instance stmt = stmt.var types += evaluator.eval_statement(stmt, seek_name=unicode(self.name_str)) if check_instance is not None: # class renames types = [ er.InstanceElement(evaluator, check_instance, a, True) if isinstance(a, (er.Function, pr.Function)) else a for a in types ] return types
def _remove_statements(self, stmt, name): """ This is the part where statements are being stripped. Due to lazy evaluation, statements like a = func; b = a; b() have to be evaluated. """ evaluator = self._evaluator types = [] # Remove the statement docstr stuff for now, that has to be # implemented with the evaluator class. #if stmt.docstr: #res_new.append(stmt) check_instance = None if isinstance(stmt, er.InstanceElement) and stmt.is_class_var: check_instance = stmt.instance stmt = stmt.var types += evaluator.eval_statement(stmt, seek_name=unicode(self.name_str)) # check for `except X as y` usages, because y needs to be instantiated. p = stmt.parent # TODO this looks really hacky, improve parser representation! if isinstance(p, pr.Flow) and p.command == 'except' \ and p.inputs and p.inputs[0].as_names == [name]: # TODO check for types that are not classes and add it to the # static analysis report. types = list(chain.from_iterable( evaluator.execute(t) for t in types)) if check_instance is not None: # class renames types = [er.InstanceElement(evaluator, check_instance, a, True) if isinstance(a, (er.Function, pr.Function)) else a for a in types] return types
def _check_array_additions(evaluator, compare_array, module, is_list): """ Checks if a `pr.Array` has "add" statements: >>> a = [""] >>> a.append(1) """ if not settings.dynamic_array_additions or isinstance( module, compiled.CompiledObject): return [] def check_calls(calls, add_name): """ Calls are processed here. The part before the call is searched and compared with the original Array. """ result = [] for c in calls: call_path = list(c.generate_call_path()) call_path_simple = [ unicode(n) if isinstance(n, pr.NamePart) else n for n in call_path ] separate_index = call_path_simple.index(add_name) if add_name == call_path_simple[-1] or separate_index == 0: # this means that there is no execution -> [].append # or the keyword is at the start -> append() continue backtrack_path = iter(call_path[:separate_index]) position = c.start_pos scope = c.get_parent_until(pr.IsScope) found = evaluator.eval_call_path(backtrack_path, scope, position) if not compare_array in found: continue params = call_path[separate_index + 1] if not params.values: continue # no params: just ignore it if add_name in ['append', 'add']: for param in params: result += evaluator.eval_statement(param) elif add_name in ['insert']: try: second_param = params[1] except IndexError: continue else: result += evaluator.eval_statement(second_param) elif add_name in ['extend', 'update']: for param in params: iterators = evaluator.eval_statement(param) result += get_iterator_types(iterators) return result from jedi.evaluate import representation as er def get_execution_parent(element, *stop_classes): """ Used to get an Instance/FunctionExecution parent """ if isinstance(element, Array): stmt = element._array.parent else: # is an Instance with an ArrayInstance inside stmt = element.var_args[0].var_args.parent if isinstance(stmt, er.InstanceElement): stop_classes = list(stop_classes) + [er.Function] return stmt.get_parent_until(stop_classes) temp_param_add = settings.dynamic_params_for_other_modules settings.dynamic_params_for_other_modules = False search_names = ['append', 'extend', 'insert'] if is_list else \ ['add', 'update'] comp_arr_parent = get_execution_parent(compare_array, er.FunctionExecution) possible_stmts = [] res = [] for n in search_names: try: possible_stmts += module.used_names[n] except KeyError: continue for stmt in possible_stmts: # Check if the original scope is an execution. If it is, one # can search for the same statement, that is in the module # dict. Executions are somewhat special in jedi, since they # literally copy the contents of a function. if isinstance(comp_arr_parent, er.FunctionExecution): stmt = comp_arr_parent. \ get_statement_for_position(stmt.start_pos) if stmt is None: continue # InstanceElements are special, because they don't get copied, # but have this wrapper around them. if isinstance(comp_arr_parent, er.InstanceElement): stmt = er.InstanceElement(comp_arr_parent.instance, stmt) if evaluator.recursion_detector.push_stmt(stmt): # check recursion continue res += check_calls(helpers.scan_statement_for_calls(stmt, n), n) evaluator.recursion_detector.pop_stmt() # reset settings settings.dynamic_params_for_other_modules = temp_param_add return res