diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index bfdfc055..4275d4be 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -176,6 +176,8 @@ class Evaluator(object): elif isinstance(element, pr.Keyword): # For False/True/None return [compiled.builtin.get_by_name(element.value)] + elif isinstance(element, pr.Lambda): + return [er.LambdaWrapper(self, element)] elif element.type == 'power': types = self._eval_atom(element.children[0]) for trailer in element.children[1:]: diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index d755cbe0..86f58d91 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -74,9 +74,12 @@ class NameFinder(object): except KeyError: return [] - if isinstance(scope, pr.CompFor): + if isinstance(scope, (pr.CompFor, pr.Lambda)): return names - names = pr.filter_after_position(names, position) + + if not (isinstance(scope, er.FunctionExecution) + and isinstance(scope.base, er.LambdaWrapper)): + names = pr.filter_after_position(names, position) names = [name for name in names if name.is_definition()] # Only the names defined in the last position are valid definitions. @@ -539,6 +542,7 @@ def get_names_of_scope(evaluator, scope, position=None, star_search=True, includ if isinstance(scope, pr.SubModule) and scope.parent or not scope.is_scope(): scope = scope.parent continue + # `pr.Class` is used, because the parent is never `Class`. # Ignore the Flows, because the classes and functions care for that. # InstanceElement of Class is ignored, if it is not the start scope. diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 88afdd88..1ae80a63 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -76,7 +76,7 @@ class Executed(pr.Base): return True def get_parent_until(self, *args, **kwargs): - return self.base.get_parent_until(*args, **kwargs) + return pr.Base.get_parent_until(self, *args, **kwargs) @common.safe_property def parent(self): @@ -512,7 +512,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)): debug.dbg('decorator end %s', f) - if isinstance(f, pr.Function): + if isinstance(f, (pr.Function, pr.Lambda)): return self return f @@ -542,6 +542,10 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)): return "" % (type(self).__name__, self.base_func, dec) +class LambdaWrapper(Function): + pass + + class LazyDict(object): def __init__(self, old_dct, copy_func): self._copy_func = copy_func @@ -588,6 +592,9 @@ class FunctionExecution(Executed): # inserted params, not in the actual execution of the function. return [] + if isinstance(func, LambdaWrapper): + return self._evaluator.eval_element(self.children[-1]) + if check_yields: types = [] returns = self.yields diff --git a/jedi/parser/__init__.py b/jedi/parser/__init__.py index 836dcb8f..2ad5d7fc 100644 --- a/jedi/parser/__init__.py +++ b/jedi/parser/__init__.py @@ -102,6 +102,7 @@ class Parser(object): 'try_stmt': pt.TryStmt, 'comp_for': pt.CompFor, 'decorator': pt.Decorator, + 'lambdef': pt.Lambda, } self.global_names = [] diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index 1ea87819..befddf4d 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -762,6 +762,30 @@ class Class(ClassOrFunc): yield self, filter_after_position(self.get_defined_names(), position) +def _create_params(function, lst): + if not lst: + return [] + if is_node(lst[0], 'typedargslist', 'varargslist'): + params = [] + iterator = iter(lst[0].children) + for n in iterator: + stars = 0 + if n in ('*', '**'): + stars = len(n.value) + n = next(iterator) + + op = next(iterator, None) + if op == '=': + default = next(iterator) + next(iterator, None) + else: + default = None + params.append(Param(n, function, default, stars)) + return params + else: + return [Param(lst[0], function)] + + class Function(ClassOrFunc): """ Used to store the parsed contents of a python function. @@ -778,36 +802,13 @@ class Function(ClassOrFunc): def __init__(self, children): super(Function, self).__init__(children) self.listeners = set() # not used here, but in evaluation. - self.params = self._params() + lst = self.children[2].children[1:-1] # After `def foo` + self.params = _create_params(self, lst) @property def name(self): return self.children[1] # First token after `def` - def _params(self): - node = self.children[2].children[1:-1] # After `def foo` - if not node: - return [] - if is_node(node[0], 'typedargslist'): - params = [] - iterator = iter(node[0].children) - for n in iterator: - stars = 0 - if n in ('*', '**'): - stars = len(n.value) - n = next(iterator) - - op = next(iterator, None) - if op == '=': - default = next(iterator) - next(iterator, None) - else: - default = None - params.append(Param(n, self, default, stars)) - return params - else: - return [Param(node[0], self)] - @property def yields(self): # TODO This is incorrect, yields are also possible in a statement. @@ -871,13 +872,25 @@ class Function(ClassOrFunc): class Lambda(Function): - def __init__(self, module, params, start_pos, parent): - super(Lambda, self).__init__(module, None, params, start_pos, None) - self.parent = parent + """ + Lambdas are basically trimmed functions, so give it the same interface. + """ + def __init__(self, children): + super(Function, self).__init__(children) + self.listeners = set() # not used here, but in evaluation. + lst = self.children[1:-2] # After `def foo` + self.params = _create_params(self, lst) + self.names_dict = dict((str(param.name), [param.name]) + for param in self.params) + + def is_generator(self): + return False + + def yields(self): + return [] def __repr__(self): - return "<%s @%s (%s-%s)>" % (type(self).__name__, self.start_pos[0], - self.start_pos[1], self.end_pos[1]) + return "<%s@%s>" % (self.__class__.__name__, self.start_pos) class Flow(Simple):