forked from VimPlug/jedi
Fix dynamic param tests.
This commit is contained in:
@@ -7,7 +7,7 @@ from jedi.api import helpers
|
||||
from jedi.evaluate import imports
|
||||
from jedi.api import keywords
|
||||
from jedi.evaluate import compiled
|
||||
from jedi.evaluate.helpers import call_of_leaf
|
||||
from jedi.evaluate.helpers import evaluate_call_of_leaf
|
||||
from jedi.evaluate.filters import get_global_filters
|
||||
|
||||
|
||||
@@ -164,8 +164,7 @@ class Completion:
|
||||
return list(self._get_class_context_completions(is_function=True))
|
||||
elif symbol_names[-1] == 'trailer' and nodes[-1] == '.':
|
||||
dot = self._module_node.get_leaf_for_position(self._position)
|
||||
atom_expr = call_of_leaf(dot.get_previous_leaf())
|
||||
completion_names += self._trailer_completions(atom_expr)
|
||||
completion_names += self._trailer_completions(dot.get_previous_leaf())
|
||||
else:
|
||||
completion_names += self._global_completions()
|
||||
completion_names += self._get_class_context_completions(is_function=False)
|
||||
@@ -194,10 +193,12 @@ class Completion:
|
||||
completion_names += filter.values()
|
||||
return completion_names
|
||||
|
||||
def _trailer_completions(self, atom_expr):
|
||||
def _trailer_completions(self, previous_leaf):
|
||||
user_context = get_user_scope(self._module_context, self._position)
|
||||
evaluation_context = self._evaluator.create_context(self._module_context, atom_expr)
|
||||
contexts = self._evaluator.eval_element(evaluation_context, atom_expr)
|
||||
evaluation_context = self._evaluator.create_context(
|
||||
self._module_context, previous_leaf
|
||||
)
|
||||
contexts = evaluate_call_of_leaf(evaluation_context, previous_leaf)
|
||||
completion_names = []
|
||||
debug.dbg('trailer completion contexts: %s', contexts)
|
||||
for context in contexts:
|
||||
|
||||
@@ -5,7 +5,7 @@ import re
|
||||
from collections import namedtuple
|
||||
|
||||
from jedi._compatibility import u
|
||||
from jedi.evaluate.helpers import call_of_leaf
|
||||
from jedi.evaluate.helpers import evaluate_call_of_leaf
|
||||
from jedi import parser
|
||||
from jedi.parser import tokenize
|
||||
from jedi.cache import time_cache
|
||||
@@ -198,16 +198,12 @@ def evaluate_goto_definition(evaluator, context, leaf):
|
||||
# magic itself.
|
||||
return evaluator.goto_definitions(context, leaf)
|
||||
|
||||
node = None
|
||||
parent = leaf.parent
|
||||
if parent.type == 'atom':
|
||||
node = leaf.parent
|
||||
return context.eval_node(leaf.parent)
|
||||
elif parent.type == 'trailer':
|
||||
node = call_of_leaf(leaf)
|
||||
|
||||
if node is None:
|
||||
return []
|
||||
return evaluator.eval_element(context, node)
|
||||
return evaluate_call_of_leaf(context, leaf)
|
||||
return []
|
||||
|
||||
|
||||
CallSignatureDetails = namedtuple(
|
||||
|
||||
@@ -429,8 +429,7 @@ class Evaluator(object):
|
||||
elif def_.type in ('import_from', 'import_name'):
|
||||
return imports.ImportWrapper(context, name).follow()
|
||||
|
||||
call = helpers.call_of_leaf(name)
|
||||
return self.eval_element(context, call)
|
||||
return helpers.evaluate_call_of_leaf(context, name)
|
||||
|
||||
def goto(self, context, name):
|
||||
def resolve_implicit_imports(names):
|
||||
@@ -522,7 +521,7 @@ class Evaluator(object):
|
||||
raise DeprecationWarning
|
||||
return element
|
||||
|
||||
def create_context(self, module_context, node):
|
||||
def create_context(self, base_context, node):
|
||||
def from_scope_node(scope_node, child_is_funcdef=None):
|
||||
is_funcdef = scope_node.type == 'funcdef'
|
||||
parent_context = None
|
||||
@@ -531,8 +530,8 @@ class Evaluator(object):
|
||||
parent_context = from_scope_node(parent_scope, child_is_funcdef=is_funcdef)
|
||||
|
||||
# TODO this whole procedure just ignores decorators
|
||||
if scope_node == module_context.module_node:
|
||||
return module_context
|
||||
if scope_node == base_node:
|
||||
return base_context
|
||||
elif is_funcdef:
|
||||
if isinstance(parent_context, AnonymousInstance):
|
||||
return AnonymousInstanceFunctionExecution(
|
||||
@@ -550,6 +549,8 @@ class Evaluator(object):
|
||||
else:
|
||||
return class_context
|
||||
|
||||
base_node = base_context.get_node()
|
||||
|
||||
if node.is_scope():
|
||||
scope_node = node
|
||||
else:
|
||||
|
||||
@@ -43,6 +43,9 @@ class Context(object):
|
||||
def eval_stmt(self, stmt, seek_name=None):
|
||||
return self.evaluator.eval_statement(self, stmt, seek_name)
|
||||
|
||||
def eval_trailer(self, types, trailer):
|
||||
return self.evaluator.eval_trailer(self, types, trailer)
|
||||
|
||||
|
||||
class TreeContext(Context):
|
||||
pass
|
||||
|
||||
@@ -16,9 +16,7 @@ It works as follows:
|
||||
- search for function calls named ``foo``
|
||||
- execute these calls and check the input. This work with a ``ParamListener``.
|
||||
"""
|
||||
from itertools import chain
|
||||
|
||||
from jedi._compatibility import unicode
|
||||
from jedi.parser import tree
|
||||
from jedi import settings
|
||||
from jedi import debug
|
||||
@@ -26,7 +24,6 @@ from jedi.evaluate.cache import memoize_default
|
||||
from jedi.evaluate import imports
|
||||
from jedi.evaluate.param import TreeArguments, create_default_param
|
||||
from jedi.common import to_list, unite
|
||||
from jedi.evaluate import context
|
||||
|
||||
|
||||
MAX_PARAM_SEARCHES = 20
|
||||
@@ -103,33 +100,21 @@ def _search_function_executions(evaluator, module_context, funcdef):
|
||||
"""
|
||||
from jedi.evaluate import representation as er
|
||||
|
||||
def get_possible_nodes(module_context, func_name):
|
||||
if not isinstance(module_context, er.ModuleContext):
|
||||
return
|
||||
try:
|
||||
names = module_context.module_node.used_names[func_name]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
for name in names:
|
||||
bracket = name.get_next_leaf()
|
||||
trailer = bracket.parent
|
||||
if trailer.type == 'trailer' and bracket == '(':
|
||||
yield name, trailer
|
||||
|
||||
func_name = unicode(funcdef.name)
|
||||
func_string_name = funcdef.name.value
|
||||
compare_node = funcdef
|
||||
if func_name == '__init__':
|
||||
if func_string_name == '__init__':
|
||||
cls = funcdef.get_parent_scope()
|
||||
if isinstance(cls, tree.Class):
|
||||
func_name = unicode(cls.name)
|
||||
func_string_name = cls.name.value
|
||||
compare_node = cls
|
||||
|
||||
found_executions = False
|
||||
i = 0
|
||||
for for_mod_context in imports.get_modules_containing_name(
|
||||
evaluator, [module_context], func_name):
|
||||
for name, trailer in get_possible_nodes(for_mod_context, func_name):
|
||||
evaluator, [module_context], func_string_name):
|
||||
if not isinstance(module_context, er.ModuleContext):
|
||||
return
|
||||
for name, trailer in _get_possible_nodes(for_mod_context, func_string_name):
|
||||
i += 1
|
||||
|
||||
# This is a simple way to stop Jedi's dynamic param recursion
|
||||
@@ -138,23 +123,79 @@ def _search_function_executions(evaluator, module_context, funcdef):
|
||||
if i * evaluator.dynamic_params_depth > MAX_PARAM_SEARCHES:
|
||||
return
|
||||
|
||||
random_context = evaluator.create_context(module_context, name)
|
||||
for value in evaluator.goto_definitions(random_context, name):
|
||||
value_node = value.get_node()
|
||||
if compare_node == value_node:
|
||||
arglist = trailer.children[1]
|
||||
if arglist == ')':
|
||||
arglist = ()
|
||||
args = TreeArguments(evaluator, random_context, arglist, trailer)
|
||||
yield er.FunctionExecutionContext(
|
||||
evaluator,
|
||||
value.parent_context,
|
||||
value_node,
|
||||
args
|
||||
)
|
||||
found_executions = True
|
||||
random_context = evaluator.create_context(for_mod_context, name)
|
||||
for function_execution in _check_name_for_execution(
|
||||
evaluator, random_context, compare_node, name, trailer):
|
||||
found_executions = True
|
||||
yield function_execution
|
||||
|
||||
# If there are results after processing a module, we're probably
|
||||
# good to process. This is a speed optimization.
|
||||
if found_executions:
|
||||
return
|
||||
|
||||
|
||||
def _get_possible_nodes(module_context, func_string_name):
|
||||
try:
|
||||
names = module_context.module_node.used_names[func_string_name]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
for name in names:
|
||||
bracket = name.get_next_leaf()
|
||||
trailer = bracket.parent
|
||||
if trailer.type == 'trailer' and bracket == '(':
|
||||
yield name, trailer
|
||||
|
||||
|
||||
def _check_name_for_execution(evaluator, context, compare_node, name, trailer):
|
||||
from jedi.evaluate import representation as er, instance
|
||||
|
||||
def create_func_excs():
|
||||
arglist = trailer.children[1]
|
||||
if arglist == ')':
|
||||
arglist = ()
|
||||
args = TreeArguments(evaluator, context, arglist, trailer)
|
||||
if value_node.type == 'funcdef':
|
||||
yield value.get_function_execution(args)
|
||||
else:
|
||||
created_instance = instance.TreeInstance(
|
||||
evaluator,
|
||||
value.parent_context,
|
||||
value,
|
||||
args
|
||||
)
|
||||
for execution in created_instance.create_init_executions():
|
||||
yield execution
|
||||
|
||||
for value in evaluator.goto_definitions(context, name):
|
||||
value_node = value.get_node()
|
||||
if compare_node == value_node:
|
||||
for func_execution in create_func_excs():
|
||||
yield func_execution
|
||||
elif isinstance(value.parent_context, er.FunctionExecutionContext) and \
|
||||
compare_node.type == 'funcdef':
|
||||
# Here we're trying to find decorators by checking the first
|
||||
# parameter. It's not very generic though. Should find a better
|
||||
# solution that also applies to nested decorators.
|
||||
params = value.parent_context.get_params()
|
||||
if len(params) != 1:
|
||||
continue
|
||||
values = params[0].infer()
|
||||
nodes = [value.get_node() for value in values]
|
||||
if nodes == [compare_node]:
|
||||
# Found a decorator.
|
||||
module_context = context.get_root_context()
|
||||
execution_context = next(create_func_excs())
|
||||
for name, trailer in _get_possible_nodes(module_context, params[0].string_name):
|
||||
if value_node.start_pos < name.start_pos < value_node.end_pos:
|
||||
random_context = evaluator.create_context(execution_context, name)
|
||||
iterator = _check_name_for_execution(
|
||||
evaluator,
|
||||
random_context,
|
||||
compare_node,
|
||||
name,
|
||||
trailer
|
||||
)
|
||||
for function_execution in iterator:
|
||||
yield function_execution
|
||||
|
||||
@@ -2,6 +2,7 @@ import copy
|
||||
from itertools import chain
|
||||
|
||||
from jedi.parser import tree
|
||||
from jedi.common import unite
|
||||
|
||||
|
||||
def deep_ast_copy(obj, parent=None, new_elements=None):
|
||||
@@ -64,11 +65,10 @@ def deep_ast_copy(obj, parent=None, new_elements=None):
|
||||
|
||||
if parent is not None:
|
||||
new_obj.parent = parent
|
||||
raise NotImplementedError
|
||||
return new_obj
|
||||
|
||||
|
||||
def call_of_leaf(leaf, cut_own_trailer=False):
|
||||
def evaluate_call_of_leaf(context, leaf, cut_own_trailer=False):
|
||||
"""
|
||||
Creates a "call" node that consist of all ``trailer`` and ``power``
|
||||
objects. E.g. if you call it with ``append``::
|
||||
@@ -90,8 +90,8 @@ def call_of_leaf(leaf, cut_own_trailer=False):
|
||||
# we should not match anything more than x.
|
||||
if trailer.type != 'trailer' or leaf not in (trailer.children[0], trailer.children[-1]):
|
||||
if trailer.type == 'atom':
|
||||
return trailer
|
||||
return leaf
|
||||
return context.eval_node(trailer)
|
||||
return context.eval_node(leaf)
|
||||
|
||||
power = trailer.parent
|
||||
index = power.children.index(trailer)
|
||||
@@ -100,21 +100,25 @@ def call_of_leaf(leaf, cut_own_trailer=False):
|
||||
else:
|
||||
cut = index + 1
|
||||
|
||||
new_power = copy.copy(power)
|
||||
new_power.children = list(new_power.children)
|
||||
new_power.children[cut:] = []
|
||||
values = context.eval_node(power.children[0])
|
||||
for trailer in power.children[1:cut]:
|
||||
values = context.eval_trailer(values, trailer)
|
||||
return values
|
||||
|
||||
if power.type == 'error_node':
|
||||
# TODO delete
|
||||
'''
|
||||
if new_power.type == 'error_node':
|
||||
start = index
|
||||
while True:
|
||||
start -= 1
|
||||
if power.children[start].type != 'trailer':
|
||||
if new_power.children[start].type != 'trailer':
|
||||
break
|
||||
transformed = tree.Node('power', power.children[start:])
|
||||
transformed.parent = power.parent
|
||||
transformed = tree.Node('power', new_power.children[start:])
|
||||
transformed.parent = new_power.parent
|
||||
return transformed
|
||||
'''
|
||||
|
||||
return power
|
||||
return new_power
|
||||
|
||||
|
||||
def get_names_of_node(node):
|
||||
|
||||
@@ -142,6 +142,19 @@ class AbstractInstanceContext(Context):
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
def _create_init_execution(self, class_context, func_node):
|
||||
return InstanceFunctionExecution(
|
||||
self,
|
||||
class_context.parent_context,
|
||||
func_node,
|
||||
self.var_args
|
||||
)
|
||||
|
||||
def create_init_executions(self):
|
||||
for name in self.get_function_slot_names('__init__'):
|
||||
if isinstance(name, LazyInstanceName):
|
||||
yield self._create_init_execution(name.class_context, name.tree_name.parent)
|
||||
|
||||
@memoize_default()
|
||||
def create_instance_context(self, class_context, node):
|
||||
if node.parent.type in ('funcdef', 'classdef'):
|
||||
@@ -153,12 +166,7 @@ class AbstractInstanceContext(Context):
|
||||
parent_context = self.create_instance_context(class_context, scope)
|
||||
if scope.type == 'funcdef':
|
||||
if scope.name.value == '__init__' and parent_context == class_context:
|
||||
return InstanceFunctionExecution(
|
||||
self,
|
||||
class_context.parent_context,
|
||||
scope,
|
||||
self.var_args
|
||||
)
|
||||
return self._create_init_execution(class_context, scope)
|
||||
else:
|
||||
return AnonymousInstanceFunctionExecution(
|
||||
self,
|
||||
@@ -230,7 +238,7 @@ class CompiledInstanceClassFilter(compiled.CompiledObjectFilter):
|
||||
return self.name_class(self._evaluator, self._instance, self._compiled_obj, name)
|
||||
|
||||
|
||||
class BoundMethod(Context):
|
||||
class BoundMethod(object):
|
||||
def __init__(self, instance, class_context, function):
|
||||
self._instance = instance
|
||||
self._class_context = class_context
|
||||
@@ -239,13 +247,16 @@ class BoundMethod(Context):
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._function, name)
|
||||
|
||||
def py__call__(self, var_args):
|
||||
function_execution = InstanceFunctionExecution(
|
||||
def get_function_execution(self, arguments):
|
||||
return InstanceFunctionExecution(
|
||||
self._instance,
|
||||
self.parent_context,
|
||||
self._function.funcdef,
|
||||
var_args
|
||||
arguments
|
||||
)
|
||||
|
||||
def py__call__(self, arguments):
|
||||
function_execution = self.get_function_execution(arguments)
|
||||
return self._function.infer_function_execution(function_execution)
|
||||
|
||||
def __repr__(self):
|
||||
@@ -265,19 +276,19 @@ class LazyInstanceName(filters.TreeNameDefinition):
|
||||
"""
|
||||
def __init__(self, instance, class_context, tree_name):
|
||||
self._instance = instance
|
||||
self._class_context = class_context
|
||||
self.class_context = class_context
|
||||
self.tree_name = tree_name
|
||||
|
||||
@property
|
||||
def parent_context(self):
|
||||
return self._instance.create_instance_context(self._class_context, self.tree_name)
|
||||
return self._instance.create_instance_context(self.class_context, self.tree_name)
|
||||
|
||||
|
||||
class LazyInstanceClassName(LazyInstanceName):
|
||||
def infer(self):
|
||||
for v in super(LazyInstanceClassName, self).infer():
|
||||
if isinstance(v, er.FunctionContext):
|
||||
yield BoundMethod(self._instance, self._class_context, v)
|
||||
yield BoundMethod(self._instance, self.class_context, v)
|
||||
else:
|
||||
yield v
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ class Comprehension(AbstractSequence):
|
||||
# InstanceElement anyway, I don't care.
|
||||
node = node.var
|
||||
last_comp = list(comp_for.get_comp_fors())[-1]
|
||||
raise NotImplementedError('should not need to copy...')
|
||||
#TODO raise NotImplementedError('should not need to copy...')
|
||||
return helpers.deep_ast_copy(node, parent=last_comp)
|
||||
|
||||
def _nested(self, comp_fors):
|
||||
|
||||
@@ -78,6 +78,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
|
||||
This class is used to evaluate instances.
|
||||
"""
|
||||
def __init__(self, evaluator, parent_context, class_context, var_args, is_generated=False):
|
||||
raise DeprecationWarning
|
||||
super(Instance, self).__init__(evaluator, parent_context, var_args)
|
||||
# Generated instances are classes that are just generated by self
|
||||
# (No var_args) used.
|
||||
@@ -562,14 +563,17 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrappe
|
||||
else:
|
||||
return function_execution.get_return_values()
|
||||
|
||||
@Python3Method
|
||||
def py__call__(self, params):
|
||||
function_execution = FunctionExecutionContext(
|
||||
def get_function_execution(self, arguments):
|
||||
return FunctionExecutionContext(
|
||||
self.evaluator,
|
||||
self.parent_context,
|
||||
self.base,
|
||||
params
|
||||
arguments
|
||||
)
|
||||
|
||||
@Python3Method
|
||||
def py__call__(self, arguments):
|
||||
function_execution = self.get_function_execution(arguments)
|
||||
return self.infer_function_execution(function_execution)
|
||||
|
||||
def py__class__(self):
|
||||
|
||||
@@ -56,11 +56,11 @@ def func(c):
|
||||
return c
|
||||
|
||||
#? str()
|
||||
func("str")
|
||||
func("something")
|
||||
|
||||
@def_func
|
||||
def func(c=1):
|
||||
#? int() float()
|
||||
#? float()
|
||||
return c
|
||||
|
||||
func(1.0)
|
||||
|
||||
Reference in New Issue
Block a user