diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 3ba4bda8..b332fa8d 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -77,6 +77,7 @@ from jedi.evaluate import compiled from jedi.evaluate import precedence from jedi.evaluate import param from jedi.evaluate import helpers +from jedi.evaluate.context import Context class Evaluator(object): @@ -110,8 +111,7 @@ class Evaluator(object): self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self) def wrap(self, element, parent_context): - if isinstance(element, (er.Wrapper, er.InstanceElement, - er.ModuleContext, er.FunctionExecution, er.Instance, compiled.CompiledObject)) or element is None: + if isinstance(element, Context) or element is None: # TODO this is so ugly, please refactor. return element @@ -328,7 +328,7 @@ class Evaluator(object): elif element.type == 'eval_input': types = self._eval_element_not_cached(context, element.children[0]) else: - types = precedence.calculate_children(self, element.children) + types = precedence.calculate_children(self, context, element.children) debug.dbg('eval_element result %s', types) return types @@ -341,13 +341,13 @@ class Evaluator(object): if isinstance(atom, tree.Name): # This is the first global lookup. stmt = atom.get_definition() - if isinstance(context, er.FunctionExecution): - # Adjust scope: If the name is not in the suite, it's a param - # default or annotation and will be resolved as part of the - # parent scope. - colon = scope.children.index(':') - if atom.start_pos < scope.children[colon + 1].start_pos: - scope = scope.get_parent_scope() + #if isinstance(context, er.FunctionExecution): + ## Adjust scope: If the name is not in the suite, it's a param + ## default or annotation and will be resolved as part of the + ## parent scope. + #colon = scope.children.index(':') + #if atom.start_pos < scope.children[colon + 1].start_pos: + ##scope = scope.get_parent_scope() if isinstance(stmt, tree.CompFor): stmt = stmt.get_parent_until((tree.ClassOrFunc, tree.ExprStmt)) if stmt.type != 'expr_stmt': diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index 466d4a21..f0b8d6a8 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -56,6 +56,15 @@ class TreeNameDefinition(ContextName): return _name_to_types(self.parent_context._evaluator, self.parent_context, self.name, None) +class ParamName(ContextName): + def __init__(self, parent_context, name): + self.parent_context = parent_context + self.name = name + + def infer(self): + return set() + + class AbstractFilter(object): _until_position = None @@ -89,9 +98,9 @@ class AbstractUsedNamesFilter(AbstractFilter): except KeyError: return [] - return self._convert_to_names(self._filter(names)) + return self._convert_names(self._filter(names)) - def _convert_to_names(self, names): + def _convert_names(self, names): return [TreeNameDefinition(self._context, name) for name in names] def values(self): @@ -127,7 +136,7 @@ class ParserTreeFilter(AbstractUsedNamesFilter): class FunctionExecutionFilter(ParserTreeFilter): - def __init__(self, evaluator, context, parser_scope, executed_function, param_by_name, + def __init__(self, evaluator, context, parser_scope, param_by_name, until_position=None, origin_scope=None): super(FunctionExecutionFilter, self).__init__( evaluator, @@ -136,16 +145,15 @@ class FunctionExecutionFilter(ParserTreeFilter): until_position, origin_scope ) - self._executed_function = executed_function - self._param_by_name = param_by_name - def _filter(self, names): - names = super(FunctionExecutionFilter, self)._filter(names) - - names = [self._executed_function.name_for_position(name.start_pos) for name in names] - names = [self._param_by_name(str(name)) if search_ancestor(name, 'param') else name - for name in names] - return names + def _convert_names(self, names): + for name in names: + param = search_ancestor(name, 'param') + if param: + #yield self.context._param_by_name(str(name)) + yield ParamName(self._context, name) + else: + yield TreeNameDefinition(self._context, name) class GlobalNameFilter(AbstractUsedNamesFilter): diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 9ef8e359..01dd55bc 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -295,19 +295,19 @@ class NameFinder(object): types = set() # Add isinstance and other if/assert knowledge. - if isinstance(self.name_str, tree.Name): - # Ignore FunctionExecution parents for now. - flow_scope = self.name_str - until = flow_scope.get_parent_until(er.FunctionExecution) - while not isinstance(until, er.FunctionExecution): - flow_scope = flow_scope.get_parent_scope(include_flows=True) - if flow_scope is None: - break - # TODO check if result is in scope -> no evaluation necessary - n = check_flow_information(self._evaluator, flow_scope, - self.name_str, self.position) - if n: - return n + #if isinstance(self.name_str, tree.Name): + ## Ignore FunctionExecution parents for now. + #flow_scope = self.name_str + #until = flow_scope.get_parent_until(er.FunctionExecution) + #while not isinstance(until, er.FunctionExecution): + #flow_scope = flow_scope.get_parent_scope(include_flows=True) + #if flow_scope is None: + #break + ## TODO check if result is in scope -> no evaluation necessary + #n = check_flow_information(self._evaluator, flow_scope, + #self.name_str, self.position) + #if n: + #return n for name in names: new_types = name.infer() diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 749936d7..d8d4bbdf 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -83,9 +83,22 @@ def follow_param(evaluator, param): return _evaluate_for_annotation(evaluator, annotation) +def py__annotations__(funcdef): + return_annotation = funcdef.annotation() + if return_annotation: + dct = {'return': return_annotation} + else: + dct = {} + for function_param in funcdef.params: + param_annotation = function_param.annotation() + if param_annotation is not None: + dct[function_param.name.value] = param_annotation + return dct + + @memoize_default(None, evaluator_is_first_arg=True) def find_return_types(evaluator, func): - annotation = func.py__annotations__().get("return", None) + annotation = py__annotations__(func).get("return", None) return _evaluate_for_annotation(evaluator, annotation) diff --git a/jedi/evaluate/precedence.py b/jedi/evaluate/precedence.py index 5225aa68..81bf29d8 100644 --- a/jedi/evaluate/precedence.py +++ b/jedi/evaluate/precedence.py @@ -30,19 +30,19 @@ def literals_to_types(evaluator, result): if is_literal(typ): # Literals are only valid as long as the operations are # correct. Otherwise add a value-free instance. - cls = builtin_from_name(evaluator, typ.name.value) + cls = builtin_from_name(evaluator, typ.name.string_name) new_result |= evaluator.execute(cls) else: new_result.add(typ) return new_result -def calculate_children(evaluator, children): +def calculate_children(evaluator, context, children): """ Calculate a list of children with operators. """ iterator = iter(children) - types = evaluator.eval_element(next(iterator)) + types = context.eval_node(next(iterator)) for operator in iterator: right = next(iterator) if tree.is_node(operator, 'comp_op'): # not in / is not @@ -53,14 +53,14 @@ def calculate_children(evaluator, children): left_bools = set([left.py__bool__() for left in types]) if left_bools == set([True]): if operator == 'and': - types = evaluator.eval_element(right) + types = context.eval_node(right) elif left_bools == set([False]): if operator != 'and': - types = evaluator.eval_element(right) + types = context.eval_node(right) # Otherwise continue, because of uncertainty. else: types = calculate(evaluator, types, operator, - evaluator.eval_element(right)) + context.eval_node(right)) debug.dbg('calculate_children types %s', types) return types diff --git a/jedi/evaluate/recursion.py b/jedi/evaluate/recursion.py index bc8fb3f5..c75b0e66 100644 --- a/jedi/evaluate/recursion.py +++ b/jedi/evaluate/recursion.py @@ -92,6 +92,7 @@ class _RecursionNode(object): def execution_recursion_decorator(func): + return func # TODO remove def run(execution, **kwargs): detector = execution._evaluator.execution_recursion_detector if detector.push_execution(execution): @@ -130,9 +131,6 @@ class ExecutionRecursionDetector(object): self.recursion_level -= 1 def push_execution(self, execution): - self.execution_funcs.add(execution.base) - self.parent_execution_funcs.append(execution.base) - return True # Remove in_par_execution_funcs = execution.base in self.parent_execution_funcs in_execution_funcs = execution.base in self.execution_funcs self.recursion_level += 1 diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 3442ffff..a5bb156f 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -57,25 +57,21 @@ from jedi.evaluate import flow_analysis from jedi.evaluate import imports from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \ GlobalNameFilter, DictFilter, ContextName -from jedi.evaluate.context import Context +from jedi.evaluate.context import TreeContext -class Executed(Context): +class Executed(TreeContext): """ An instance is also an executable - because __init__ is called :param var_args: The param input array, consist of a parser node or a list. """ - def __init__(self, evaluator, parent_context, base, var_args): + def __init__(self, evaluator, parent_context, var_args): super(Executed, self).__init__(evaluator, parent_context=parent_context) - self.base = base self.var_args = var_args def is_scope(self): return True - def get_parent_until(self, *args, **kwargs): - return tree.Base.get_parent_until(self, *args, **kwargs) - class Instance(use_metaclass(CachedMetaClass, Executed)): """ @@ -132,7 +128,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)): func = self.get_subscope_by_name('__init__') except KeyError: return None - return FunctionExecution(self._evaluator, self, func, self.var_args) + return FunctionExecutionContext(self._evaluator, self, func, self.var_args) def _get_func_self_name(self, func): """ @@ -257,12 +253,6 @@ class Instance(use_metaclass(CachedMetaClass, Executed)): def name(self): return ContextName(self, self.base.name) - def __getattr__(self, name): - if name not in ['start_pos', 'end_pos', 'get_imports', 'type', - 'doc', 'raw_doc']: - return super(Instance, self).__getattribute__(name) - return getattr(self.base, name) - def __repr__(self): dec = '' if self.decorates is not None: @@ -380,7 +370,7 @@ def get_instance_el(evaluator, instance, var, is_class_var=False): return InstanceName(var, parent) elif var.type != 'funcdef' \ and isinstance(var, (Instance, compiled.CompiledObject, tree.Leaf, - tree.Module, FunctionExecution)): + tree.Module, FunctionExecutionContext)): return var var = evaluator.wrap(var) @@ -451,9 +441,6 @@ class InstanceElement(use_metaclass(CachedMetaClass, tree.Base)): return get_instance_el(self._evaluator, self.instance, self.var[index], self.is_class_var) - def __getattr__(self, name): - return getattr(self.var, name) - def isinstance(self, *cls): return isinstance(self.var, cls) @@ -496,7 +483,7 @@ class Wrapper(tree.Base): return ContextName(self, name) -class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)): +class ClassContext(use_metaclass(CachedMetaClass, TreeContext, Wrapper)): """ This class is not only important to extend `tree.Class`, it is also a important for descriptors (if the descriptor methods are evaluated or not). @@ -602,7 +589,7 @@ class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)): return "" % (type(self).__name__, self.base) -class Function(use_metaclass(CachedMetaClass, Context, Wrapper)): +class Function(use_metaclass(CachedMetaClass, TreeContext, Wrapper)): """ Needed because of decorators. Decorators are evaluated here. """ @@ -690,21 +677,12 @@ class Function(use_metaclass(CachedMetaClass, Context, Wrapper)): if self.base.is_generator(): return set([iterable.Generator(self._evaluator, self, params)]) else: - return FunctionExecution(self._evaluator, self.parent_context, self, params).get_return_types() - - @memoize_default() - def py__annotations__(self): - parser_func = self.base - return_annotation = parser_func.annotation() - if return_annotation: - dct = {'return': return_annotation} - else: - dct = {} - for function_param in parser_func.params: - param_annotation = function_param.annotation() - if param_annotation is not None: - dct[function_param.name.value] = param_annotation - return dct + return FunctionExecutionContext( + self._evaluator, + self.parent_context, + self.base, + params + ).get_return_types() def py__class__(self): # This differentiation is only necessary for Python2. Python3 does not @@ -715,9 +693,6 @@ class Function(use_metaclass(CachedMetaClass, Context, Wrapper)): name = 'FUNCTION_CLASS' return compiled.get_special_object(self._evaluator, name) - def __getattr__(self, name): - return getattr(self.base_func, name) - def __repr__(self): dec = '' if self.decorates is not None: @@ -730,7 +705,7 @@ class LambdaWrapper(Function): return self -class FunctionExecution(Executed): +class FunctionExecutionContext(Executed): """ This class is used to evaluate functions and their returns. @@ -741,10 +716,9 @@ class FunctionExecution(Executed): """ type = 'funcdef' - def __init__(self, evaluator, parent_context, base, var_args): - super(FunctionExecution, self).__init__(evaluator, parent_context, base, var_args) - self._copy_dict = {} - self._original_function = funcdef = base + def __init__(self, evaluator, parent_context, funcdef, var_args): + super(FunctionExecutionContext, self).__init__(evaluator, parent_context, var_args) + self._funcdef = funcdef if isinstance(funcdef, mixed.MixedObject): # The extra information in mixed is not needed anymore. We can just # unpack it and give it the tree object. @@ -763,11 +737,11 @@ class FunctionExecution(Executed): @memoize_default(default=set()) @recursion.execution_recursion_decorator def get_return_types(self, check_yields=False): - func = self.base - - if func.isinstance(LambdaWrapper): + funcdef = self._funcdef + if funcdef.type in ('lambdef', 'lambdef_nocond'): return self._evaluator.eval_element(self.children[-1]) + """ if func.listeners: # Feed the listeners, with the params. for listener in func.listeners: @@ -776,16 +750,19 @@ class FunctionExecution(Executed): # execution ongoing. In this case Jedi is interested in the # inserted params, not in the actual execution of the function. return set() +""" if check_yields: types = set() - returns = self.yields + returns = funcdef.yields else: - returns = self.returns - types = set(docstrings.find_return_types(self._evaluator, func)) - types |= set(pep0484.find_return_types(self._evaluator, func)) + returns = funcdef.returns + types = set(docstrings.find_return_types(self._evaluator, funcdef)) + types |= set(pep0484.find_return_types(self._evaluator, funcdef)) for r in returns: + types |= self.eval_node(r.children[1]) + continue # TODO REMOVE check = flow_analysis.break_check(self._evaluator, self, r) if check is flow_analysis.UNREACHABLE: debug.dbg('Return unreachable: %s', r) @@ -793,26 +770,26 @@ class FunctionExecution(Executed): if check_yields: types |= iterable.unite(self._eval_yield(r)) else: - types |= self._evaluator.eval_element(r.children[1]) + types |= self.eval_node(r.children[1]) if check is flow_analysis.REACHABLE: debug.dbg('Return reachable: %s', r) break return types def _eval_yield(self, yield_expr): - element = yield_expr.children[1] - if element.type == 'yield_arg': + node = yield_expr.children[1] + if node.type == 'yield_arg': # It must be a yield from. - yield_from_types = self._evaluator.eval_element(element.children[1]) - for result in iterable.py__iter__(self._evaluator, yield_from_types, element): + yield_from_types = self.eval_node(node) + for result in iterable.py__iter__(self._evaluator, yield_from_types, node): yield result else: - yield self._evaluator.eval_element(element) + yield self.eval_node(node) @recursion.execution_recursion_decorator def get_yield_types(self): yields = self.yields - stopAt = tree.ForStmt, tree.WhileStmt, FunctionExecution, tree.IfStmt + stopAt = tree.ForStmt, tree.WhileStmt, tree.IfStmt for_parents = [(x, x.get_parent_until((stopAt))) for x in yields] # Calculate if the yields are placed within the same for loop. @@ -857,55 +834,20 @@ class FunctionExecution(Executed): del evaluator.predefined_if_name_dict_dict[for_stmt] def get_filters(self, search_global, until_position=None, origin_scope=None): - yield FunctionExecutionFilter(self._evaluator, self, self._original_function, - self._copied_funcdef, + yield FunctionExecutionFilter(self._evaluator, self, self._funcdef, self.param_by_name, until_position, origin_scope=origin_scope) @memoize_default(default=NO_DEFAULT) def _get_params(self): - """ - This returns the params for an TODO and is injected as a - 'hack' into the tree.Function class. - This needs to be here, because Instance can have __init__ functions, - which act the same way as normal functions. - """ - return param.get_params(self._evaluator, self.base, self.var_args) + return param.get_params(self._evaluator, self._funcdef, self.var_args) def param_by_name(self, name): return [n for n in self._get_params() if str(n) == name][0] - def name_for_position(self, position): - return tree.Function.name_for_position(self, position) - - def __getattr__(self, name): - if name not in ['start_pos', 'end_pos', 'imports', 'name', 'type']: - return super(FunctionExecution, self).__getattribute__(name) - return getattr(self.base, name) - - @common.safe_property - @memoize_default() - def returns(self): - return tree.Scope._search_in_scope(self, tree.ReturnStmt) - - @common.safe_property - @memoize_default() - def yields(self): - return tree.Scope._search_in_scope(self, tree.YieldExpr) - - @common.safe_property - @memoize_default() - def statements(self): - return tree.Scope._search_in_scope(self, tree.ExprStmt) - - @common.safe_property - @memoize_default() - def subscopes(self): - return tree.Scope._search_in_scope(self, tree.Scope) - def __repr__(self): - return "<%s of %s>" % (type(self).__name__, self.base) + return "<%s of %s>" % (type(self).__name__, self._funcdef) class GlobalName(helpers.FakeName): @@ -919,7 +861,7 @@ class GlobalName(helpers.FakeName): name.start_pos, is_definition=True) -class ModuleContext(use_metaclass(CachedMetaClass, tree.Module, Wrapper)): +class ModuleContext(use_metaclass(CachedMetaClass, TreeContext, Wrapper)): parent_context = None def __init__(self, evaluator, module, parent_module=None): @@ -1095,8 +1037,5 @@ class ModuleContext(use_metaclass(CachedMetaClass, tree.Module, Wrapper)): def py__class__(self): return compiled.get_special_object(self._evaluator, 'MODULE_CLASS') - def __getattr__(self, name): - return getattr(self._module, name) - def __repr__(self): return "<%s: %s>" % (type(self).__name__, self._module) diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index 92cff5dd..edb2c948 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -37,8 +37,8 @@ def execute(evaluator, obj, arguments): else: if obj.parent_context == evaluator.BUILTINS: module_name = 'builtins' - elif isinstance(obj.parent, tree.Module): - module_name = str(obj.parent.name) + elif isinstance(obj.parent_context, er.ModuleContext): + module_name = obj.parent_context.name.string_name else: module_name = ''