def _check_isinstance_type(context, element, search_name): try: assert element.type in ('power', 'atom_expr') # this might be removed if we analyze and, etc assert len(element.children) == 2 first, trailer = element.children assert first.type == 'name' and first.value == 'isinstance' assert trailer.type == 'trailer' and trailer.children[0] == '(' assert len(trailer.children) == 3 # arglist stuff arglist = trailer.children[1] args = TreeArguments(context.evaluator, context, arglist, trailer) param_list = list(args.unpack()) # Disallow keyword arguments assert len(param_list) == 2 (key1, lazy_context_object), (key2, lazy_context_cls) = param_list assert key1 is None and key2 is None call = helpers.call_of_leaf(search_name) is_instance_call = helpers.call_of_leaf(lazy_context_object.data) # Do a simple get_code comparison. They should just have the same code, # and everything will be all right. normalize = context.evaluator.grammar._normalize assert normalize(is_instance_call) == normalize(call) except AssertionError: return None context_set = NO_CONTEXTS for cls_or_tup in lazy_context_cls.infer(): if isinstance(cls_or_tup, iterable.Sequence) and cls_or_tup.array_type == 'tuple': for lazy_context in cls_or_tup.py__iter__(): context_set |= lazy_context.infer().execute_evaluated(context) else: context_set |= helpers.execute_evaluated(cls_or_tup) return context_set
def _check_isinstance_type(evaluator, element, search_name): try: assert element.type in ('power', 'atom_expr') # this might be removed if we analyze and, etc assert len(element.children) == 2 first, trailer = element.children assert isinstance(first, tree.Name) and first.value == 'isinstance' assert trailer.type == 'trailer' and trailer.children[0] == '(' assert len(trailer.children) == 3 # arglist stuff arglist = trailer.children[1] args = param.Arguments(evaluator, arglist, trailer) lst = list(args.unpack()) # Disallow keyword arguments assert len(lst) == 2 and lst[0][0] is None and lst[1][0] is None name = lst[0][1][0] # first argument, values, first value # Do a simple get_code comparison. They should just have the same code, # and everything will be all right. classes = lst[1][1][0] call = helpers.call_of_leaf(search_name) assert name.get_code(normalized=True) == call.get_code(normalized=True) except AssertionError: return set() result = set() for cls_or_tup in evaluator.eval_element(classes): if isinstance(cls_or_tup, iterable.Array) and cls_or_tup.type == 'tuple': for typ in unite(cls_or_tup.py__iter__()): result |= evaluator.execute(typ) else: result |= evaluator.execute(cls_or_tup) return result
def _check_isinstance_type(evaluator, element, search_name): try: assert element.type in ("power", "atom_expr") # this might be removed if we analyze and, etc assert len(element.children) == 2 first, trailer = element.children assert isinstance(first, tree.Name) and first.value == "isinstance" assert trailer.type == "trailer" and trailer.children[0] == "(" assert len(trailer.children) == 3 # arglist stuff arglist = trailer.children[1] args = param.Arguments(evaluator, arglist, trailer) lst = list(args.unpack()) # Disallow keyword arguments assert len(lst) == 2 and lst[0][0] is None and lst[1][0] is None name = lst[0][1][0] # first argument, values, first value # Do a simple get_code comparison. They should just have the same code, # and everything will be all right. classes = lst[1][1][0] call = helpers.call_of_leaf(search_name) assert name.get_code(normalized=True) == call.get_code(normalized=True) except AssertionError: return set() result = set() for cls_or_tup in evaluator.eval_element(classes): if isinstance(cls_or_tup, iterable.Array) and cls_or_tup.type == "tuple": for typ in unite(cls_or_tup.py__iter__()): result |= evaluator.execute(typ) else: result |= evaluator.execute(cls_or_tup) return result
def test_call_of_leaf_in_brackets(): s = dedent(""" x = 1 type(x) """) last_x = names(s, references=True, definitions=False)[-1] name = last_x._name call = helpers.call_of_leaf(name) assert call == name
def _check_isinstance_type(context, element, search_name): try: assert element.type in ('power', 'atom_expr') # this might be removed if we analyze and, etc assert len(element.children) == 2 first, trailer = element.children assert first.type == 'name' and first.value == 'isinstance' assert trailer.type == 'trailer' and trailer.children[0] == '(' assert len(trailer.children) == 3 # arglist stuff arglist = trailer.children[1] args = TreeArguments(context.evaluator, context, arglist, trailer) param_list = list(args.unpack()) # Disallow keyword arguments assert len(param_list) == 2 (key1, lazy_context_object), (key2, lazy_context_cls) = param_list assert key1 is None and key2 is None call = helpers.call_of_leaf(search_name) is_instance_call = helpers.call_of_leaf(lazy_context_object.data) # Do a simple get_code comparison. They should just have the same code, # and everything will be all right. normalize = context.evaluator.grammar._normalize assert normalize(is_instance_call) == normalize(call) except AssertionError: return None context_set = ContextSet() for cls_or_tup in lazy_context_cls.infer(): if isinstance(cls_or_tup, iterable.AbstractIterable) and \ cls_or_tup.array_type == 'tuple': for lazy_context in cls_or_tup.py__iter__(): for context in lazy_context.infer(): context_set |= context.execute_evaluated() else: context_set |= cls_or_tup.execute_evaluated() return context_set
def evaluate_goto_definition(evaluator, leaf): if leaf.type == 'name': # In case of a name we can just use goto_definition which does all the # magic itself. return evaluator.goto_definitions(leaf) node = None parent = leaf.parent if parent.type == 'atom': node = leaf.parent elif parent.type == 'trailer': node = call_of_leaf(leaf) if node is None: return [] return evaluator.eval_element(node)
def goto_definitions(self, name): def_ = name.get_definition() is_simple_name = name.parent.type not in ("power", "trailer") if is_simple_name: if name.parent.type in ("file_input", "classdef", "funcdef"): return [self.wrap(name.parent)] if def_.type == "expr_stmt" and name in def_.get_defined_names(): return self.eval_statement(def_, name) elif def_.type == "for_stmt": container_types = self.eval_element(def_.children[3]) for_types = iterable.py__iter__types(self, container_types, def_.children[3]) return finder.check_tuple_assignments(self, for_types, name) elif def_.type in ("import_from", "import_name"): return imports.ImportWrapper(self, name).follow() call = helpers.call_of_leaf(name) return self.eval_element(call)
def goto_definitions(self, name): def_ = name.get_definition() is_simple_name = name.parent.type not in ('power', 'trailer') if is_simple_name: if name.parent.type in ('file_input', 'classdef', 'funcdef'): return [self.wrap(name.parent)] if def_.type == 'expr_stmt' and name in def_.get_defined_names(): return self.eval_statement(def_, name) elif def_.type == 'for_stmt': container_types = self.eval_element(def_.children[3]) for_types = iterable.py__iter__types(self, container_types, def_.children[3]) return finder.check_tuple_assignments(self, for_types, name) elif def_.type in ('import_from', 'import_name'): return imports.ImportWrapper(self, name).follow() call = helpers.call_of_leaf(name) return self.eval_element(call)
def goto_definitions(self, name): def_ = name.get_definition() is_simple_name = name.parent.type not in ('power', 'trailer') if is_simple_name: if name.parent.type == 'classdef' and name.parent.name == name: return [self.wrap(name.parent)] if name.parent.type in ('file_input', 'funcdef'): return [self.wrap(name.parent)] if def_.type == 'expr_stmt' and name in def_.get_defined_names(): return self.eval_statement(def_, name) elif def_.type == 'for_stmt': container_types = self.eval_element(def_.children[3]) for_types = iterable.py__iter__types(self, container_types, def_.children[3]) return finder.check_tuple_assignments(self, for_types, name) elif def_.type in ('import_from', 'import_name'): return imports.ImportWrapper(self, name).follow() call = helpers.call_of_leaf(name) return self.eval_element(call)
def goto(self, name): def resolve_implicit_imports(names): for name in names: if isinstance(name.parent, helpers.FakeImport): # Those are implicit imports. s = imports.ImportWrapper(self, name) for n in s.follow(is_goto=True): yield n else: yield name stmt = name.get_definition() par = name.parent if par.type == 'argument' and par.children[1] == '=' and par.children[ 0] == name: # Named param goto. trailer = par.parent if trailer.type == 'arglist': trailer = trailer.parent if trailer.type != 'classdef': if trailer.type == 'decorator': types = self.eval_element(trailer.children[1]) else: i = trailer.parent.children.index(trailer) to_evaluate = trailer.parent.children[:i] types = self.eval_element(to_evaluate[0]) for trailer in to_evaluate[1:]: types = self.eval_trailer(types, trailer) param_names = [] for typ in types: try: params = typ.params except AttributeError: pass else: param_names += [ param.name for param in params if param.name.value == name.value ] return param_names elif isinstance(par, tree.ExprStmt) and name in par.get_defined_names(): # Only take the parent, because if it's more complicated than just # a name it's something you can "goto" again. return [name] elif isinstance( par, (tree.Param, tree.Function, tree.Class)) and par.name is name: return [name] elif isinstance(stmt, tree.Import): modules = imports.ImportWrapper(self, name).follow(is_goto=True) return list(resolve_implicit_imports(modules)) elif par.type == 'dotted_name': # Is a decorator. index = par.children.index(name) if index > 0: new_dotted = helpers.deep_ast_copy(par) new_dotted.children[index - 1:] = [] types = self.eval_element(new_dotted) return resolve_implicit_imports( iterable.unite( self.find_types(typ, name, is_goto=True) for typ in types)) scope = name.get_parent_scope() if tree.is_node(par, 'trailer') and par.children[0] == '.': call = helpers.call_of_leaf(name, cut_own_trailer=True) types = self.eval_element(call) return resolve_implicit_imports( iterable.unite( self.find_types(typ, name, is_goto=True) for typ in types)) else: if stmt.type != 'expr_stmt': # We only need to adjust the start_pos for statements, because # there the name cannot be used. stmt = name return self.find_types(scope, name, stmt.start_pos, search_global=True, is_goto=True)
def _get_context_completions(self): """ Analyzes the context that a completion is made in and decides what to return. Technically this works by generating a parser stack and analysing the current stack for possible grammar nodes. Possible enhancements: - global/nonlocal search global - yield from / raise from <- could be only exceptions/generators - In args: */**: no completion - In params (also lambda): no completion before = """ grammar = self._evaluator.grammar try: self.stack = helpers.get_stack_at_position( grammar, self._code_lines, self._module, self._position ) except helpers.OnErrorLeaf as e: self.stack = None if e.error_leaf.value == '.': # After ErrorLeaf's that are dots, we will not do any # completions since this probably just confuses the user. return [] # If we don't have a context, just use global completion. return self._global_completions() allowed_keywords, allowed_tokens = \ helpers.get_possible_completion_types(grammar, self.stack) completion_names = list(self._get_keyword_completion_names(allowed_keywords)) if token.NAME in allowed_tokens: # This means that we actually have to do type inference. symbol_names = list(self.stack.get_node_names(grammar)) nodes = list(self.stack.get_nodes()) if "import_stmt" in symbol_names: level = 0 only_modules = True level, names = self._parse_dotted_names(nodes) if "import_from" in symbol_names: if 'import' in nodes: only_modules = False else: assert "import_name" in symbol_names completion_names += self._get_importer_names( names, level, only_modules ) elif nodes and nodes[-1] in ('as', 'def', 'class'): # No completions for ``with x as foo`` and ``import x as foo``. # Also true for defining names as a class or function. return list(self._get_class_context_completions(is_function=True)) elif symbol_names[-1] == 'trailer' and nodes[-1] == '.': dot = self._module.get_leaf_for_position(self._position) atom_expr = call_of_leaf(dot.get_previous_leaf()) completion_names += self._trailer_completions(atom_expr) else: completion_names += self._global_completions() completion_names += self._get_class_context_completions(is_function=False) if 'trailer' in symbol_names: call_signatures = self._call_signatures_method() completion_names += get_call_signature_param_names(call_signatures) return completion_names
def goto(self, name): def resolve_implicit_imports(names): for name in names: if isinstance(name.parent, helpers.FakeImport): # Those are implicit imports. s = imports.ImportWrapper(self, name) for n in s.follow(is_goto=True): yield n else: yield name stmt = name.get_definition() par = name.parent if par.type == 'argument' and par.children[1] == '=' and par.children[0] == name: # Named param goto. trailer = par.parent if trailer.type == 'arglist': trailer = trailer.parent if trailer.type != 'classdef': if trailer.type == 'decorator': types = self.eval_element(trailer.children[1]) else: i = trailer.parent.children.index(trailer) to_evaluate = trailer.parent.children[:i] types = self.eval_element(to_evaluate[0]) for trailer in to_evaluate[1:]: types = self.eval_trailer(types, trailer) param_names = [] for typ in types: try: params = typ.params except AttributeError: pass else: param_names += [param.name for param in params if param.name.value == name.value] return param_names elif isinstance(par, tree.ExprStmt) and name in par.get_defined_names(): # Only take the parent, because if it's more complicated than just # a name it's something you can "goto" again. return [name] elif isinstance(par, (tree.Param, tree.Function, tree.Class)) and par.name is name: return [name] elif isinstance(stmt, tree.Import): modules = imports.ImportWrapper(self, name).follow(is_goto=True) return list(resolve_implicit_imports(modules)) elif par.type == 'dotted_name': # Is a decorator. index = par.children.index(name) if index > 0: new_dotted = helpers.deep_ast_copy(par) new_dotted.children[index - 1:] = [] types = self.eval_element(new_dotted) return resolve_implicit_imports(iterable.unite( self.find_types(typ, name, is_goto=True) for typ in types )) scope = name.get_parent_scope() if tree.is_node(par, 'trailer') and par.children[0] == '.': call = helpers.call_of_leaf(name, cut_own_trailer=True) types = self.eval_element(call) return resolve_implicit_imports(iterable.unite( self.find_types(typ, name, is_goto=True) for typ in types )) else: if stmt.type != 'expr_stmt': # We only need to adjust the start_pos for statements, because # there the name cannot be used. stmt = name return self.find_types(scope, name, stmt.start_pos, search_global=True, is_goto=True)
def _check_array_additions(evaluator, compare_array, module, is_list): """ Checks if a `Array` has "add" (append, insert, extend) statements: >>> a = [""] >>> a.append(1) """ debug.dbg('Dynamic array search for %s' % compare_array, color='MAGENTA') if not settings.dynamic_array_additions or isinstance( module, compiled.CompiledObject): debug.dbg('Dynamic array search aborted.', color='MAGENTA') return set() def check_additions(arglist, add_name): params = list(param.Arguments(evaluator, arglist).unpack()) result = set() if add_name in ['insert']: params = params[1:] if add_name in ['append', 'add', 'insert']: for key, nodes in params: result |= unite(evaluator.eval_element(node) for node in nodes) elif add_name in ['extend', 'update']: for key, nodes in params: for node in nodes: types = evaluator.eval_element(node) result |= py__iter__types(evaluator, types, node) return result from jedi.evaluate import representation as er, param def get_execution_parent(element): """ Used to get an Instance/FunctionExecution parent """ if isinstance(element, Array): node = element.atom else: # Is an Instance with an # Arguments([AlreadyEvaluated([_ArrayInstance])]) inside # Yeah... I know... It's complicated ;-) node = list(element.var_args.argument_node[0])[0].var_args.trailer if isinstance(node, er.InstanceElement) or node is None: return node return node.get_parent_until(er.FunctionExecution) 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) added_types = set() for add_name in search_names: try: possible_names = module.used_names[add_name] except KeyError: continue else: for name in possible_names: # 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): if comp_arr_parent.start_pos < name.start_pos < comp_arr_parent.end_pos: name = comp_arr_parent.name_for_position( name.start_pos) else: # Don't check definitions that are not defined in the # same function. This is not "proper" anyway. It also # improves Jedi's speed for array lookups, since we # don't have to check the whole source tree anymore. continue trailer = name.parent power = trailer.parent trailer_pos = power.children.index(trailer) try: execution_trailer = power.children[trailer_pos + 1] except IndexError: continue else: if execution_trailer.type != 'trailer' \ or execution_trailer.children[0] != '(' \ or execution_trailer.children[1] == ')': continue power = helpers.call_of_leaf(name, cut_own_trailer=True) # InstanceElements are special, because they don't get copied, # but have this wrapper around them. if isinstance(comp_arr_parent, er.InstanceElement): power = er.get_instance_el(evaluator, comp_arr_parent.instance, power) if evaluator.recursion_detector.push_stmt(power): # Check for recursion. Possible by using 'extend' in # combination with function calls. continue try: if compare_array in evaluator.eval_element(power): # The arrays match. Now add the results added_types |= check_additions( execution_trailer.children[1], add_name) finally: evaluator.recursion_detector.pop_stmt() # reset settings settings.dynamic_params_for_other_modules = temp_param_add debug.dbg('Dynamic array result %s' % added_types, color='MAGENTA') return added_types
def _get_context_completions(self): """ Analyzes the context that a completion is made in and decides what to return. Technically this works by generating a parser stack and analysing the current stack for possible grammar nodes. Possible enhancements: - global/nonlocal search global - yield from / raise from <- could be only exceptions/generators - In args: */**: no completion - In params (also lambda): no completion before = """ grammar = self._evaluator.grammar try: stack = helpers.get_stack_at_position( grammar, self._code_lines, self._module, self._position ) except helpers.OnErrorLeaf as e: if e.error_leaf.value == '.': # After ErrorLeaf's that are dots, we will not do any # completions since this probably just confuses the user. return [] # If we don't have a context, just use global completion. return self._global_completions() allowed_keywords, allowed_tokens = \ helpers.get_possible_completion_types(grammar, stack) completion_names = list(self._get_keyword_completion_names(allowed_keywords)) if token.NAME in allowed_tokens: # This means that we actually have to do type inference. symbol_names = list(stack.get_node_names(grammar)) nodes = list(stack.get_nodes()) if "import_stmt" in symbol_names: level = 0 only_modules = True level, names = self._parse_dotted_names(nodes) if "import_from" in symbol_names: if 'import' in nodes: only_modules = False else: assert "import_name" in symbol_names completion_names += self._get_importer_names( names, level, only_modules ) elif nodes and nodes[-1] in ('as', 'def', 'class'): # No completions for ``with x as foo`` and ``import x as foo``. # Also true for defining names as a class or function. return [] elif symbol_names[-1] == 'trailer' and nodes[-1] == '.': dot = self._module.get_leaf_for_position(self._position) atom_expr = call_of_leaf(dot.get_previous_leaf()) completion_names += self._trailer_completions(atom_expr) else: completion_names += self._global_completions() if 'trailer' in symbol_names: call_signatures = self._call_signatures_method() completion_names += get_call_signature_param_names(call_signatures) return completion_names
def _check_array_additions(evaluator, compare_array, module, is_list): """ Checks if a `Array` has "add" (append, insert, extend) statements: >>> a = [""] >>> a.append(1) """ debug.dbg('Dynamic array search for %s' % compare_array, color='MAGENTA') if not settings.dynamic_array_additions or isinstance(module, compiled.CompiledObject): debug.dbg('Dynamic array search aborted.', color='MAGENTA') return set() def check_additions(arglist, add_name): params = list(param.Arguments(evaluator, arglist).unpack()) result = set() if add_name in ['insert']: params = params[1:] if add_name in ['append', 'add', 'insert']: for key, nodes in params: result |= unite(evaluator.eval_element(node) for node in nodes) elif add_name in ['extend', 'update']: for key, nodes in params: for node in nodes: types = evaluator.eval_element(node) result |= py__iter__types(evaluator, types, node) return result from jedi.evaluate import representation as er, param def get_execution_parent(element): """ Used to get an Instance/FunctionExecution parent """ if isinstance(element, Array): node = element.atom else: # Is an Instance with an # Arguments([AlreadyEvaluated([_ArrayInstance])]) inside # Yeah... I know... It's complicated ;-) node = list(element.var_args.argument_node[0])[0].var_args.trailer if isinstance(node, er.InstanceElement): return node return node.get_parent_until(er.FunctionExecution) 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) added_types = set() for add_name in search_names: try: possible_names = module.used_names[add_name] except KeyError: continue else: for name in possible_names: # 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): if comp_arr_parent.start_pos < name.start_pos < comp_arr_parent.end_pos: name = comp_arr_parent.name_for_position(name.start_pos) else: # Don't check definitions that are not defined in the # same function. This is not "proper" anyway. It also # improves Jedi's speed for array lookups, since we # don't have to check the whole source tree anymore. continue trailer = name.parent power = trailer.parent trailer_pos = power.children.index(trailer) try: execution_trailer = power.children[trailer_pos + 1] except IndexError: continue else: if execution_trailer.type != 'trailer' \ or execution_trailer.children[0] != '(' \ or execution_trailer.children[1] == ')': continue power = helpers.call_of_leaf(name, cut_own_trailer=True) # InstanceElements are special, because they don't get copied, # but have this wrapper around them. if isinstance(comp_arr_parent, er.InstanceElement): power = er.get_instance_el(evaluator, comp_arr_parent.instance, power) if evaluator.recursion_detector.push_stmt(power): # Check for recursion. Possible by using 'extend' in # combination with function calls. continue if compare_array in evaluator.eval_element(power): # The arrays match. Now add the results added_types |= check_additions(execution_trailer.children[1], add_name) evaluator.recursion_detector.pop_stmt() # reset settings settings.dynamic_params_for_other_modules = temp_param_add debug.dbg('Dynamic array result %s' % added_types, color='MAGENTA') return added_types