1
0
forked from VimPlug/jedi

Trying to get dyanmic params working.

This commit is contained in:
Dave Halter
2016-11-02 11:11:21 +01:00
parent aaaa3c24a5
commit cd874cb052
5 changed files with 71 additions and 70 deletions

View File

@@ -387,7 +387,7 @@ class Evaluator(object):
return new_types
@debug.increase_indent
def execute(self, obj, arguments=None):
def execute(self, obj, arguments):
if not isinstance(arguments, param.AbstractArguments):
raise NotImplementedError
arguments = param.Arguments(self, arguments)

View File

@@ -24,6 +24,8 @@ from jedi import settings
from jedi import debug
from jedi.evaluate.cache import memoize_default
from jedi.evaluate import imports
from jedi.evaluate.param import TreeArguments
from jedi.common import to_list, unite
MAX_PARAM_SEARCHES = 20
@@ -40,8 +42,19 @@ class ParamListener(object):
self.param_possibilities += params
class MergedExecutedParams(object):
"""
Simulates being a parameter while actually just being multiple params.
"""
def __init__(self, executed_params):
self._executed_params = executed_params
def infer(self):
return unite(p.infer() for p in self._executed_params)
@debug.increase_indent
def search_params(evaluator, param):
def search_params(evaluator, funcdef):
"""
A dynamic search for param values. If you try to complete a type:
@@ -59,21 +72,23 @@ def search_params(evaluator, param):
evaluator.dynamic_params_depth += 1
try:
func = param.get_parent_until(tree.Function)
debug.dbg('Dynamic param search for %s in %s.', param, str(func.name), color='MAGENTA')
# Compare the param names.
names = [n for n in search_function_call(evaluator, func)
if n.value == param.name.value]
debug.dbg('Dynamic param search in %s.', funcdef.name.value, color='MAGENTA')
function_executions = _search_function_executions(evaluator, funcdef)
zipped_params = zip(*(
function_execution.get_params()
for function_execution in function_executions
))
params = [MergedExecutedParams(executed_params) for executed_params in zipped_params]
# Evaluate the ExecutedParams to types.
result = set(chain.from_iterable(n.parent.eval(evaluator) for n in names))
debug.dbg('Dynamic param result %s', result, color='MAGENTA')
return result
debug.dbg('Dynamic param result finished', color='MAGENTA')
return params
finally:
evaluator.dynamic_params_depth -= 1
@memoize_default([], evaluator_is_first_arg=True)
def search_function_call(evaluator, func):
@to_list
def _search_function_executions(evaluator, funcdef):
"""
Returns a list of param names.
"""
@@ -91,63 +106,45 @@ def search_function_call(evaluator, func):
if trailer.type == 'trailer' and bracket == '(':
yield name, trailer
def undecorate(typ):
# We have to remove decorators, because they are not the
# "original" functions, this way we can easily compare.
# At the same time we also have to remove InstanceElements.
return typ
# TODO remove
if typ.isinstance(er.Function, er.Instance) \
and typ.decorates is not None:
return typ.decorates
elif isinstance(typ, er.InstanceElement):
return typ.var
else:
return typ
current_module = func.get_parent_until()
func_name = unicode(func.name)
compare = func
current_module = funcdef.get_parent_until()
func_name = unicode(funcdef.name)
compare_node = funcdef
if func_name == '__init__':
cls = func.get_parent_scope()
raise NotImplementedError
cls = funcdef.get_parent_scope()
if isinstance(cls, tree.Class):
func_name = unicode(cls.name)
compare = cls
compare_node = cls
# add the listener
listener = ParamListener()
func.listeners.add(listener)
found_executions = False
i = 0
for mod in imports.get_modules_containing_name(evaluator, [current_module], func_name):
module_context = er.ModuleContext(evaluator, mod)
for name, trailer in get_possible_nodes(mod, func_name):
i += 1
try:
result = []
i = 0
for mod in imports.get_modules_containing_name(evaluator, [current_module], func_name):
for name, trailer in get_possible_nodes(mod, func_name):
i += 1
# This is a simple way to stop Jedi's dynamic param recursion
# from going wild: The deeper Jedi's in the recursion, the less
# code should be evaluated.
if i * evaluator.dynamic_params_depth > MAX_PARAM_SEARCHES:
return
# This is a simple way to stop Jedi's dynamic param recursion
# from going wild: The deeper Jedi's in the recursin, the less
# code should be evaluated.
if i * evaluator.dynamic_params_depth > MAX_PARAM_SEARCHES:
return listener.param_possibilities
random_context = evaluator.create_context(module_context, name)
for context in evaluator.goto_definitions(random_context, name):
if compare_node == context.funcdef:
arglist = trailer.children[1]
if arglist == ')':
arglist = ()
args = TreeArguments(evaluator, random_context, arglist, trailer)
yield er.FunctionExecutionContext(
evaluator,
context.parent_context,
context.funcdef,
args
)
found_executions = True
context = evaluator.create_context(name)
for typ in evaluator.goto_definitions(context, name):
undecorated = undecorate(typ)
# TODO really?
if evaluator.wrap(context, compare) == undecorated:
# Only if we have the correct function we execute
# it, otherwise just ignore it.
evaluator.eval_trailer([typ], trailer)
result = listener.param_possibilities
# If there are results after processing a module, we're probably
# good to process.
if result:
return result
finally:
# cleanup: remove the listener; important: should not stick.
func.listeners.remove(listener)
return set()
# If there are results after processing a module, we're probably
# good to process. This is a speed optimization.
if found_executions:
return

View File

@@ -72,7 +72,7 @@ class ParamName(ContextName):
def _get_param(self):
params = self.parent_context.get_params()
return [p for p in params if p.string_name == self.string_name][0]
return params[self.name.parent.position_nr]
class AbstractFilter(object):

View File

@@ -195,7 +195,6 @@ class ValuesArguments(AbstractArguments):
class ExecutedParam(object):
"""Fake a param and give it values."""
def __init__(self, original_param, var_args, lazy_context):
assert not isinstance(lazy_context, (tuple, list))
self._original_param = original_param
self.var_args = var_args
self._lazy_context = lazy_context
@@ -209,6 +208,9 @@ class ExecutedParam(object):
# Need to use the original logic here, because it uses the parent.
return self._original_param.position_nr
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.string_name)
def _get_calling_var_args(evaluator, var_args):
old_var_args = None

View File

@@ -57,6 +57,7 @@ from jedi.evaluate import flow_analysis
from jedi.evaluate import imports
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
GlobalNameFilter, DictFilter, ContextName
from jedi.evaluate.dynamic import search_params
from jedi.evaluate import context
from jedi.evaluate.instance import TreeInstance
@@ -512,7 +513,7 @@ class ClassContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper))
raise KeyError("Couldn't find subscope.")
def __repr__(self):
return "<e%s of %s>" % (type(self).__name__, self.classdef)
return "<%s of %s>" % (type(self).__name__, self.classdef)
class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper)):
@@ -522,7 +523,7 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrappe
def __init__(self, evaluator, parent_context, funcdef):
""" This should not be called directly """
super(FunctionContext, self).__init__(evaluator, parent_context)
self.base = self.base_func = funcdef
self.base = self.base_func = self.funcdef = funcdef
def names_dicts(self, search_global):
if search_global:
@@ -563,7 +564,7 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrappe
return compiled.get_special_object(self._evaluator, name)
def __repr__(self):
return "<e%s of %s>" % (type(self).__name__, self.base_func)
return "<%s of %s>" % (type(self).__name__, self.base_func)
class LambdaWrapper(FunctionContext):
@@ -717,7 +718,8 @@ class AnonymousFunctionExecution(FunctionExecutionContext):
@memoize_default(default=NO_DEFAULT)
def get_params(self):
return []
# We need to do a dynamic search here.
return search_params(self._evaluator, self.funcdef)
class GlobalName(helpers.FakeName):