diff --git a/jedi/api/classes.py b/jedi/api/classes.py index 54d5bc42..691c9acf 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -312,12 +312,10 @@ class BaseDefinition(object): return '.'.join(path if path[0] else path[1:]) def goto_assignments(self): - try: - tree_name = self._name.tree_name - except AttributeError: + if self._name.tree_name is None: return self - defs = self._evaluator.goto(self._name.parent_context, tree_name) + defs = self._evaluator.goto(self._name.parent_context, self._name.tree_name) return [Definition(self._evaluator, d) for d in defs] @memoize_method @@ -559,36 +557,34 @@ class Definition(BaseDefinition): """ typ = self.type - try: - tree_name = self._name.tree_name - except AttributeError: - pass - else: - definition = tree_name.get_definition() + tree_name = self._name.tree_name + if typ in ('function', 'class', 'module') or tree_name is None: + if typ == 'function': + # For the description we want a short and a pythonic way. + typ = 'def' + return typ + ' ' + self._name.string_name - try: - first_leaf = definition.first_leaf() - except AttributeError: - # `d` is already a Leaf (Name). - first_leaf = definition - # Remove the prefix, because that's not what we want for get_code - # here. - old, first_leaf.prefix = first_leaf.prefix, '' - try: - txt = definition.get_code() - finally: - first_leaf.prefix = old - # Delete comments: - txt = re.sub('#[^\n]+\n', ' ', txt) - # Delete multi spaces/newlines - txt = re.sub('\s+', ' ', txt).strip() - if typ == 'param': - txt = typ + ' ' + txt - return txt - if typ == 'function': - # For the description we want a short and a pythonic way. - typ = 'def' - return typ + ' ' + self._name.string_name + definition = tree_name.get_definition() + + try: + first_leaf = definition.first_leaf() + except AttributeError: + # `d` is already a Leaf (Name). + first_leaf = definition + # Remove the prefix, because that's not what we want for get_code + # here. + old, first_leaf.prefix = first_leaf.prefix, '' + try: + txt = definition.get_code() + finally: + first_leaf.prefix = old + # Delete comments: + txt = re.sub('#[^\n]+\n', ' ', txt) + # Delete multi spaces/newlines + txt = re.sub('\s+', ' ', txt).strip() + if typ == 'param': + txt = typ + ' ' + txt + return txt # TODO DELETE @@ -664,12 +660,10 @@ class Definition(BaseDefinition): Returns True, if defined as a name in a statement, function or class. Returns False, if it's a reference to such a definition. """ - try: - tree_name = self._name.tree_name - except AttributeError: + if self._name.tree_name is None: return True else: - return tree_name.is_definition() + return self._name.tree_name.is_definition() def __eq__(self, other): return self._name.start_pos == other._name.start_pos \ @@ -708,22 +702,15 @@ class CallSignature(Definition): return i if self.params: param_name = self.params[-1]._name - try: - tree_name = param_name.tree_name - except AttributeError: - pass - else: - if tree_name.get_definition().stars == 2: + if param_name.tree_name is not None: + if param_name.tree_name.get_definition().stars == 2: return i return None if self._index >= len(self.params): for i, param in enumerate(self.params): - try: - tree_name = param._name.tree_name - except AttributeError: - pass - else: + tree_name = param._name.tree_name + if tree_name is not None: # *args case if tree_name.get_definition().stars == 1: return i diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 38fb6a88..0424649f 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -79,7 +79,7 @@ from jedi.evaluate import precedence from jedi.evaluate import param from jedi.evaluate import helpers from jedi.evaluate.filters import TreeNameDefinition -from jedi.evaluate.instance import AnonymousInstance, AnonymousInstanceFunctionExecution +from jedi.evaluate.instance import AnonymousInstance, BoundMethod class Evaluator(object): @@ -489,7 +489,10 @@ class Evaluator(object): return [TreeNameDefinition(context, name)] elif isinstance(par, (tree.Param, tree.Function, tree.Class)) and par.name is name: if par.type in ('funcdef', 'classdef', 'module'): - return [context.name] + if par.type == 'funcdef': + return [context.function_context.name] + else: + return [context.name] return [TreeNameDefinition(context, name)] elif isinstance(stmt, tree.Import): module_names = imports.ImportWrapper(context, name).follow(is_goto=True) @@ -567,13 +570,17 @@ class Evaluator(object): if is_funcdef: if isinstance(parent_context, AnonymousInstance): - return AnonymousInstanceFunctionExecution( - parent_context, - parent_context.parent_context, - scope_node + func = BoundMethod( + self, parent_context, parent_context.class_context, + parent_context.parent_context, scope_node ) else: - return er.AnonymousFunctionExecution(self, parent_context, scope_node) + func = er.FunctionContext( + self, + parent_context, + scope_node + ) + return func.get_function_execution() elif scope_node.type == 'classdef': class_context = er.ClassContext(self, scope_node, parent_context) if child_is_funcdef: diff --git a/jedi/evaluate/docstrings.py b/jedi/evaluate/docstrings.py index a76355fa..9a51671a 100644 --- a/jedi/evaluate/docstrings.py +++ b/jedi/evaluate/docstrings.py @@ -143,19 +143,20 @@ def _evaluate_for_statement_string(module_context, string): return [] from jedi.evaluate.param import ValuesArguments - from jedi.evaluate.representation import FunctionExecutionContext - func_context = FunctionExecutionContext( + from jedi.evaluate.representation import FunctionContext + function_context = FunctionContext( module_context.evaluator, module_context, - funcdef, + funcdef + ) + func_execution_context = function_context.get_function_execution( ValuesArguments([]) ) - # Use the module of the param. # TODO this module is not the module of the param in case of a function # call. In that case it's the module of the function call. # stuffed with content from a function call. - return list(_execute_types_in_stmt(func_context, stmt)) + return list(_execute_types_in_stmt(func_execution_context, stmt)) def _execute_types_in_stmt(module_context, stmt): diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index d0c78a06..d7c8efa7 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -191,6 +191,7 @@ class ImportName(AbstractNameDefinition): def api_type(self): return 'module' + class SubModuleName(ImportName): def infer(self): return Importer( diff --git a/jedi/evaluate/instance.py b/jedi/evaluate/instance.py index 6dcb0538..5c89af05 100644 --- a/jedi/evaluate/instance.py +++ b/jedi/evaluate/instance.py @@ -130,10 +130,13 @@ class AbstractInstanceContext(Context): pass def _create_init_execution(self, class_context, func_node): + bound_method = BoundMethod( + self.evaluator, self, class_context, self.parent_context, func_node + ) return InstanceFunctionExecution( self, class_context.parent_context, - func_node, + bound_method, self.var_args ) @@ -155,11 +158,11 @@ class AbstractInstanceContext(Context): if scope.name.value == '__init__' and parent_context == class_context: return self._create_init_execution(class_context, scope) else: - return AnonymousInstanceFunctionExecution( - self, - class_context.parent_context, - scope, + bound_method = BoundMethod( + self.evaluator, self, class_context, + self.parent_context, scope ) + return bound_method.get_function_execution() else: raise NotImplementedError return class_context @@ -251,13 +254,13 @@ class BoundMethod(er.FunctionContext): self._instance = instance self._class_context = class_context - def get_function_execution(self, arguments): - return InstanceFunctionExecution( - self._instance, - self.parent_context, - self.funcdef, - arguments - ) + def get_function_execution(self, arguments=None): + if arguments is None: + return AnonymousInstanceFunctionExecution( + self._instance, self.parent_context, self) + else: + return InstanceFunctionExecution( + self._instance, self.parent_context, self, arguments) class CompiledBoundMethod(compiled.CompiledObject): @@ -428,17 +431,17 @@ class InstanceVarArgs(object): class InstanceFunctionExecution(er.FunctionExecutionContext): - def __init__(self, instance, parent_context, funcdef, var_args): + def __init__(self, instance, parent_context, function_context, var_args): self.instance = instance - var_args = InstanceVarArgs(instance, funcdef, var_args) + var_args = InstanceVarArgs(instance, function_context.funcdef, var_args) super(InstanceFunctionExecution, self).__init__( - instance.evaluator, parent_context, funcdef, var_args) + instance.evaluator, parent_context, function_context, var_args) class AnonymousInstanceFunctionExecution(InstanceFunctionExecution): function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter - def __init__(self, instance, parent_context, funcdef): + def __init__(self, instance, parent_context, function_context): super(AnonymousInstanceFunctionExecution, self).__init__( - instance, parent_context, funcdef, None) + instance, parent_context, function_context, None) diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 2578f37e..7b504efd 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -252,13 +252,12 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)): else: return function_execution.get_return_values() - def get_function_execution(self, arguments): - return FunctionExecutionContext( - self.evaluator, - self.parent_context, - self.base, - arguments - ) + def get_function_execution(self, arguments=None): + e = self.evaluator + if arguments is None: + return AnonymousFunctionExecution(e, self.parent_context, self) + else: + return FunctionExecutionContext(e, self.parent_context, self, arguments) def py__call__(self, arguments): function_execution = self.get_function_execution(arguments) @@ -281,7 +280,7 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)): anon = AnonymousFunctionExecution( self.evaluator, self.parent_context, - self.funcdef + self ) return [ParamName(anon, param.name) for param in self.funcdef.params] @@ -297,12 +296,14 @@ class FunctionExecutionContext(Executed): """ function_execution_filter = FunctionExecutionFilter - def __init__(self, evaluator, parent_context, funcdef, var_args): + def __init__(self, evaluator, parent_context, function_context, var_args): super(FunctionExecutionContext, self).__init__(evaluator, parent_context, var_args) - self.funcdef = funcdef - if isinstance(funcdef, mixed.MixedObject): + self.function_context = function_context + self.funcdef = function_context.funcdef + if isinstance(function_context, mixed.MixedObject): # The extra information in mixed is not needed anymore. We can just # unpack it and give it the tree object. + raise DeprecationWarning funcdef = funcdef.definition # Just overwrite the old version. We don't need it anymore. @@ -316,7 +317,7 @@ class FunctionExecutionContext(Executed): #self._copied_funcdef = funcdef def get_node(self): - return self.funcdef + return self.function_context.funcdef @memoize_default(default=set()) @recursion.execution_recursion_decorator @@ -426,9 +427,9 @@ class FunctionExecutionContext(Executed): class AnonymousFunctionExecution(FunctionExecutionContext): - def __init__(self, evaluator, parent_context, funcdef): + def __init__(self, evaluator, parent_context, function_context): super(AnonymousFunctionExecution, self).__init__( - evaluator, parent_context, funcdef, var_args=None) + evaluator, parent_context, function_context, var_args=None) @memoize_default(default=NO_DEFAULT) def get_params(self): diff --git a/test/completion/goto.py b/test/completion/goto.py index eb9fc114..adff012d 100644 --- a/test/completion/goto.py +++ b/test/completion/goto.py @@ -22,10 +22,10 @@ cd = e #! ['module math'] import math -#! ['import math'] +#! ['module math'] math -#! ['import math'] +#! ['module math'] b = math #! ['b = math'] b @@ -148,7 +148,7 @@ mod1.a #! ['a = 1.0'] from import_tree.pkg.mod1 import a -#! ['import os'] +#! ['module os'] from .imports import os #! ['some_variable = 1']