mirror of
https://github.com/davidhalter/jedi.git
synced 2026-02-17 16:34:11 +08:00
Trying to get dyanmic params working.
This commit is contained in:
@@ -387,7 +387,7 @@ class Evaluator(object):
|
|||||||
return new_types
|
return new_types
|
||||||
|
|
||||||
@debug.increase_indent
|
@debug.increase_indent
|
||||||
def execute(self, obj, arguments=None):
|
def execute(self, obj, arguments):
|
||||||
if not isinstance(arguments, param.AbstractArguments):
|
if not isinstance(arguments, param.AbstractArguments):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
arguments = param.Arguments(self, arguments)
|
arguments = param.Arguments(self, arguments)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ from jedi import settings
|
|||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.evaluate.cache import memoize_default
|
from jedi.evaluate.cache import memoize_default
|
||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
|
from jedi.evaluate.param import TreeArguments
|
||||||
|
from jedi.common import to_list, unite
|
||||||
|
|
||||||
|
|
||||||
MAX_PARAM_SEARCHES = 20
|
MAX_PARAM_SEARCHES = 20
|
||||||
@@ -40,8 +42,19 @@ class ParamListener(object):
|
|||||||
self.param_possibilities += params
|
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
|
@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:
|
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
|
evaluator.dynamic_params_depth += 1
|
||||||
try:
|
try:
|
||||||
func = param.get_parent_until(tree.Function)
|
debug.dbg('Dynamic param search in %s.', funcdef.name.value, color='MAGENTA')
|
||||||
debug.dbg('Dynamic param search for %s in %s.', param, str(func.name), color='MAGENTA')
|
function_executions = _search_function_executions(evaluator, funcdef)
|
||||||
# Compare the param names.
|
zipped_params = zip(*(
|
||||||
names = [n for n in search_function_call(evaluator, func)
|
function_execution.get_params()
|
||||||
if n.value == param.name.value]
|
for function_execution in function_executions
|
||||||
|
))
|
||||||
|
params = [MergedExecutedParams(executed_params) for executed_params in zipped_params]
|
||||||
# Evaluate the ExecutedParams to types.
|
# Evaluate the ExecutedParams to types.
|
||||||
result = set(chain.from_iterable(n.parent.eval(evaluator) for n in names))
|
debug.dbg('Dynamic param result finished', color='MAGENTA')
|
||||||
debug.dbg('Dynamic param result %s', result, color='MAGENTA')
|
return params
|
||||||
return result
|
|
||||||
finally:
|
finally:
|
||||||
evaluator.dynamic_params_depth -= 1
|
evaluator.dynamic_params_depth -= 1
|
||||||
|
|
||||||
|
|
||||||
@memoize_default([], evaluator_is_first_arg=True)
|
@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.
|
Returns a list of param names.
|
||||||
"""
|
"""
|
||||||
@@ -91,63 +106,45 @@ def search_function_call(evaluator, func):
|
|||||||
if trailer.type == 'trailer' and bracket == '(':
|
if trailer.type == 'trailer' and bracket == '(':
|
||||||
yield name, trailer
|
yield name, trailer
|
||||||
|
|
||||||
def undecorate(typ):
|
current_module = funcdef.get_parent_until()
|
||||||
# We have to remove decorators, because they are not the
|
func_name = unicode(funcdef.name)
|
||||||
# "original" functions, this way we can easily compare.
|
compare_node = funcdef
|
||||||
# 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
|
|
||||||
if func_name == '__init__':
|
if func_name == '__init__':
|
||||||
cls = func.get_parent_scope()
|
raise NotImplementedError
|
||||||
|
cls = funcdef.get_parent_scope()
|
||||||
if isinstance(cls, tree.Class):
|
if isinstance(cls, tree.Class):
|
||||||
func_name = unicode(cls.name)
|
func_name = unicode(cls.name)
|
||||||
compare = cls
|
compare_node = cls
|
||||||
|
|
||||||
# add the listener
|
found_executions = False
|
||||||
listener = ParamListener()
|
i = 0
|
||||||
func.listeners.add(listener)
|
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:
|
# This is a simple way to stop Jedi's dynamic param recursion
|
||||||
result = []
|
# from going wild: The deeper Jedi's in the recursion, the less
|
||||||
i = 0
|
# code should be evaluated.
|
||||||
for mod in imports.get_modules_containing_name(evaluator, [current_module], func_name):
|
if i * evaluator.dynamic_params_depth > MAX_PARAM_SEARCHES:
|
||||||
for name, trailer in get_possible_nodes(mod, func_name):
|
return
|
||||||
i += 1
|
|
||||||
|
|
||||||
# This is a simple way to stop Jedi's dynamic param recursion
|
random_context = evaluator.create_context(module_context, name)
|
||||||
# from going wild: The deeper Jedi's in the recursin, the less
|
for context in evaluator.goto_definitions(random_context, name):
|
||||||
# code should be evaluated.
|
if compare_node == context.funcdef:
|
||||||
if i * evaluator.dynamic_params_depth > MAX_PARAM_SEARCHES:
|
arglist = trailer.children[1]
|
||||||
return listener.param_possibilities
|
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)
|
# If there are results after processing a module, we're probably
|
||||||
for typ in evaluator.goto_definitions(context, name):
|
# good to process. This is a speed optimization.
|
||||||
undecorated = undecorate(typ)
|
if found_executions:
|
||||||
# TODO really?
|
return
|
||||||
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()
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class ParamName(ContextName):
|
|||||||
|
|
||||||
def _get_param(self):
|
def _get_param(self):
|
||||||
params = self.parent_context.get_params()
|
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):
|
class AbstractFilter(object):
|
||||||
|
|||||||
@@ -195,7 +195,6 @@ class ValuesArguments(AbstractArguments):
|
|||||||
class ExecutedParam(object):
|
class ExecutedParam(object):
|
||||||
"""Fake a param and give it values."""
|
"""Fake a param and give it values."""
|
||||||
def __init__(self, original_param, var_args, lazy_context):
|
def __init__(self, original_param, var_args, lazy_context):
|
||||||
assert not isinstance(lazy_context, (tuple, list))
|
|
||||||
self._original_param = original_param
|
self._original_param = original_param
|
||||||
self.var_args = var_args
|
self.var_args = var_args
|
||||||
self._lazy_context = lazy_context
|
self._lazy_context = lazy_context
|
||||||
@@ -209,6 +208,9 @@ class ExecutedParam(object):
|
|||||||
# Need to use the original logic here, because it uses the parent.
|
# Need to use the original logic here, because it uses the parent.
|
||||||
return self._original_param.position_nr
|
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):
|
def _get_calling_var_args(evaluator, var_args):
|
||||||
old_var_args = None
|
old_var_args = None
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ from jedi.evaluate import flow_analysis
|
|||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
|
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
|
||||||
GlobalNameFilter, DictFilter, ContextName
|
GlobalNameFilter, DictFilter, ContextName
|
||||||
|
from jedi.evaluate.dynamic import search_params
|
||||||
from jedi.evaluate import context
|
from jedi.evaluate import context
|
||||||
from jedi.evaluate.instance import TreeInstance
|
from jedi.evaluate.instance import TreeInstance
|
||||||
|
|
||||||
@@ -512,7 +513,7 @@ class ClassContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper))
|
|||||||
raise KeyError("Couldn't find subscope.")
|
raise KeyError("Couldn't find subscope.")
|
||||||
|
|
||||||
def __repr__(self):
|
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)):
|
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):
|
def __init__(self, evaluator, parent_context, funcdef):
|
||||||
""" This should not be called directly """
|
""" This should not be called directly """
|
||||||
super(FunctionContext, self).__init__(evaluator, parent_context)
|
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):
|
def names_dicts(self, search_global):
|
||||||
if 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)
|
return compiled.get_special_object(self._evaluator, name)
|
||||||
|
|
||||||
def __repr__(self):
|
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):
|
class LambdaWrapper(FunctionContext):
|
||||||
@@ -717,7 +718,8 @@ class AnonymousFunctionExecution(FunctionExecutionContext):
|
|||||||
|
|
||||||
@memoize_default(default=NO_DEFAULT)
|
@memoize_default(default=NO_DEFAULT)
|
||||||
def get_params(self):
|
def get_params(self):
|
||||||
return []
|
# We need to do a dynamic search here.
|
||||||
|
return search_params(self._evaluator, self.funcdef)
|
||||||
|
|
||||||
|
|
||||||
class GlobalName(helpers.FakeName):
|
class GlobalName(helpers.FakeName):
|
||||||
|
|||||||
Reference in New Issue
Block a user