1
0
forked from VimPlug/jedi

Fix dynamic param tests.

This commit is contained in:
Dave Halter
2016-11-24 00:11:26 +01:00
parent 06efc8fb8c
commit 7ed1c95737
10 changed files with 149 additions and 88 deletions

View File

@@ -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:

View File

@@ -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(

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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):

View File

@@ -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):

View File

@@ -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)