diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 88c35b71..e033d115 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -61,16 +61,24 @@ class NameFinder(object): if search_global: return get_names_of_scope(self._evaluator, self.scope, self.position) else: - if self.scope.isinstance(er.Function): - return iter([(self.scope, self.scope.get_magic_function_names())]) return self.scope.scope_names_generator(self.position) - def names_dict_lookup(self, scope): + def names_dict_lookup(self, scope, position): + def get_param(el): + if isinstance(el.parent, pr.Param) or isinstance(el.parent.parent, pr.Param): + return scope.param_by_name(str(el)) + return el + try: names = scope.names_dict[str(self.name_str)] except KeyError: return [] - return [name for name in names if name.is_definition()] + names = [name for name in names if name.is_definition()] + names = pr.filter_after_position(names, position) + if isinstance(scope, er.FunctionExecution): + # Replace params + return [get_param(n) for n in names] + return names def filter_name(self, scope_names_generator, search_global=False): """ @@ -82,11 +90,17 @@ class NameFinder(object): from jedi.api.interpreter import InterpreterNamespace names = [] self.maybe_descriptor = isinstance(self.scope, er.Class) + if not search_global and self.scope.isinstance(er.Function): + return [n for n in self.scope.get_magic_function_names() + if str(n) == str(self.name_str)] + for name_list_scope, name_list in scope_names_generator: if hasattr(name_list_scope, 'names_dict'): - names = self.names_dict_lookup(name_list_scope) + names = self.names_dict_lookup(name_list_scope, self.position) if names: break + if isinstance(name_list_scope, (pr.Function, er.FunctionExecution)): + self.position = None continue break_scopes = [] diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index 6680bdc0..f05e33fb 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -15,7 +15,6 @@ def deep_ast_copy(obj, new_elements_default=None): return key_value[0] not in ('_expression_list', '_assignment_details') new_elements = new_elements_default or {} - accept = (pr.Simple, pr.Name, pr.KeywordStatement) def recursion(obj): # If it's already in the cache, just return it. @@ -52,7 +51,7 @@ def deep_ast_copy(obj, new_elements_default=None): # tree in there. items = sorted(items, key=sort_stmt) else: - items = sorted(items, key=lambda x: x[0] == '_names_dict') + items = sorted(items, key=lambda x: x[0] != 'params') # Actually copy and set attributes. new_obj = copy.copy(obj) @@ -75,7 +74,7 @@ def deep_ast_copy(obj, new_elements_default=None): setattr(new_obj, key, d) elif isinstance(value, (list, tuple)): setattr(new_obj, key, sequence_recursion(value)) - elif isinstance(value, accept): + elif isinstance(value, (pr.Simple, pr.Name)): setattr(new_obj, key, recursion(value)) return new_obj @@ -86,10 +85,10 @@ def deep_ast_copy(obj, new_elements_default=None): else: copied_array = array_obj[:] # lists, tuples, strings, unicode for i, el in enumerate(copied_array): - if isinstance(el, accept): - copied_array[i] = recursion(el) - elif isinstance(el, (tuple, list)): + if isinstance(el, (tuple, list)): copied_array[i] = sequence_recursion(el) + else: + copied_array[i] = recursion(el) if isinstance(array_obj, tuple): return tuple(copied_array) diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index f6c2294f..2836ac5d 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -559,6 +559,9 @@ class FunctionExecution(Executed): super(FunctionExecution, self).__init__(evaluator, base, *args, **kwargs) # for deep_ast_copy self._copy_dict = {base.base_func: self} + # We definitely want the params to be generated. Params are special, + # because they have been altered and are not normal "children". + self.params @memoize_default(default=()) @recursion.execution_recursion_decorator @@ -589,6 +592,11 @@ class FunctionExecution(Executed): @property @underscore_memoization def names_dict(self): + d = {} + for key, values in self.base.names_dict.items(): + d[key] = self._copy_list(values) + return d + self.base.names_dict return LazyDict(self.base.names_dict, self._copy_list) @memoize_default(default=NO_DEFAULT) @@ -601,6 +609,9 @@ class FunctionExecution(Executed): """ return param.get_params(self._evaluator, self.base, self.var_args) + def param_by_name(self, name): + return [n for n in self._get_params() if str(n) == name][0] + def get_defined_names(self): """ Call the default method with the own instance (self implements all @@ -638,6 +649,11 @@ class FunctionExecution(Executed): self._scope_copy(scope.parent) helpers.deep_ast_copy(scope, self._copy_dict) + @common.safe_property + @memoize_default([]) + def params(self): + return self._copy_list(self.base.params) + @common.safe_property @memoize_default([]) def returns(self): diff --git a/jedi/parser/representation.py b/jedi/parser/representation.py index 4bcd3844..394ac6cd 100644 --- a/jedi/parser/representation.py +++ b/jedi/parser/representation.py @@ -257,6 +257,8 @@ class Name(Leaf): stmt = self.get_definition() if isinstance(stmt, (Function, Class, Module)): return True + elif isinstance(stmt, Param): + return self == stmt.get_name() else: return isinstance(stmt, (ExprStmt, Import)) \ and self in stmt.get_defined_names() @@ -878,7 +880,7 @@ class Flow(Scope): :param start_pos: Position (line, column) of the Flow statement. :type start_pos: tuple(int, int) """ - __slots__ = ('next', 'previous', 'command', '_parent', 'inputs', 'set_vars') + __slots__ = ('next', 'previous', 'command', 'parent', 'inputs', 'set_vars') def __init__(self, module, command, inputs, start_pos): self.next = None @@ -1252,7 +1254,7 @@ class Param(Base): A helper class for functions. Read only. """ - __slots__ = ('tfpdef', 'default', 'stars', 'parent', 'annotation_stmt') + __slots__ = ('tfpdef', 'default', 'stars', 'parent') def __init__(self, tfpdef, parent, default=None, stars=0): self.tfpdef = tfpdef # tfpdef: see grammar.txt @@ -1262,6 +1264,10 @@ class Param(Base): # Here we reset the parent of our name. IMHO this is ok. self.get_name().parent = self + def annotation(self): + # Generate from tfpdef. + raise NotImplementedError + @property def children(self): return []