1
0
forked from VimPlug/jedi

Give ExecutionParams a better way of knowing what called them.

This commit is contained in:
Dave Halter
2017-04-26 09:32:47 +02:00
parent f136745a8a
commit bea28fd33f
4 changed files with 63 additions and 56 deletions

View File

@@ -52,7 +52,7 @@ class MergedExecutedParams(object):
@debug.increase_indent @debug.increase_indent
def search_params(evaluator, parent_context, funcdef): def search_params(evaluator, execution_context, 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:
@@ -71,7 +71,7 @@ def search_params(evaluator, parent_context, funcdef):
evaluator.dynamic_params_depth += 1 evaluator.dynamic_params_depth += 1
try: try:
debug.dbg('Dynamic param search in %s.', funcdef.name.value, color='MAGENTA') debug.dbg('Dynamic param search in %s.', funcdef.name.value, color='MAGENTA')
module_context = parent_context.get_root_context() module_context = execution_context.get_root_context()
function_executions = _search_function_executions( function_executions = _search_function_executions(
evaluator, evaluator,
module_context, module_context,
@@ -85,7 +85,7 @@ def search_params(evaluator, parent_context, funcdef):
params = [MergedExecutedParams(executed_params) for executed_params in zipped_params] params = [MergedExecutedParams(executed_params) for executed_params in zipped_params]
# Evaluate the ExecutedParams to types. # Evaluate the ExecutedParams to types.
else: else:
params = [create_default_param(parent_context, p) for p in funcdef.params] params = [create_default_param(execution_context, p) for p in funcdef.params]
debug.dbg('Dynamic param result finished', color='MAGENTA') debug.dbg('Dynamic param result finished', color='MAGENTA')
return params return params
finally: finally:

View File

@@ -383,14 +383,14 @@ class ParamArguments(object):
def infer(self): def infer(self):
return self._param.infer() return self._param.infer()
def __init__(self, class_context, funcdef): def __init__(self, execution_context, funcdef):
self._class_context = class_context self._execution_context = execution_context
self._funcdef = funcdef self._funcdef = funcdef
def unpack(self, func=None): def unpack(self, func=None):
params = search_params( params = search_params(
self._class_context.evaluator, self._execution_context.evaluator,
self._class_context, self._execution_context,
self._funcdef self._funcdef
) )
is_first = True is_first = True
@@ -403,8 +403,8 @@ class ParamArguments(object):
class InstanceVarArgs(object): class InstanceVarArgs(object):
def __init__(self, instance, funcdef, var_args): def __init__(self, execution_context, funcdef, var_args):
self._instance = instance self._execution_context = execution_context
self._funcdef = funcdef self._funcdef = funcdef
self._var_args = var_args self._var_args = var_args
@@ -412,12 +412,12 @@ class InstanceVarArgs(object):
def _get_var_args(self): def _get_var_args(self):
if self._var_args is None: if self._var_args is None:
# TODO this parent_context might be wrong. test?! # TODO this parent_context might be wrong. test?!
return ParamArguments(self._instance.class_context, self._funcdef) return ParamArguments(self._execution_context, self._funcdef)
return self._var_args return self._var_args
def unpack(self, func=None): def unpack(self, func=None):
yield None, LazyKnownContext(self._instance) yield None, LazyKnownContext(self._execution_context.instance)
for values in self._get_var_args().unpack(func): for values in self._get_var_args().unpack(func):
yield values yield values
@@ -431,7 +431,7 @@ class InstanceVarArgs(object):
class InstanceFunctionExecution(er.FunctionExecutionContext): class InstanceFunctionExecution(er.FunctionExecutionContext):
def __init__(self, instance, parent_context, function_context, var_args): def __init__(self, instance, parent_context, function_context, var_args):
self.instance = instance self.instance = instance
var_args = InstanceVarArgs(instance, function_context.tree_node, var_args) var_args = InstanceVarArgs(self, function_context.tree_node, var_args)
super(InstanceFunctionExecution, self).__init__( super(InstanceFunctionExecution, self).__init__(
instance.evaluator, parent_context, function_context, var_args) instance.evaluator, parent_context, function_context, var_args)

View File

@@ -61,7 +61,7 @@ class AbstractArguments():
raise ValueError raise ValueError
yield values yield values
def eval_all(self, func=None): def eval_all(self, funcdef=None):
""" """
Evaluates all arguments as a support for static analysis Evaluates all arguments as a support for static analysis
(normally Jedi). (normally Jedi).
@@ -110,12 +110,12 @@ class TreeArguments(AbstractArguments):
else: else:
yield 0, child yield 0, child
def unpack(self, func=None): def unpack(self, funcdef=None):
named_args = [] named_args = []
for star_count, el in self._split(): for star_count, el in self._split():
if star_count == 1: if star_count == 1:
arrays = self.context.eval_node(el) arrays = self.context.eval_node(el)
iterators = [_iterate_star_args(self.context, a, el, func) iterators = [_iterate_star_args(self.context, a, el, funcdef)
for a in arrays] for a in arrays]
iterators = list(iterators) iterators = list(iterators)
for values in list(zip_longest(*iterators)): for values in list(zip_longest(*iterators)):
@@ -127,7 +127,7 @@ class TreeArguments(AbstractArguments):
elif star_count == 2: elif star_count == 2:
arrays = self._evaluator.eval_element(self.context, el) arrays = self._evaluator.eval_element(self.context, el)
for dct in arrays: for dct in arrays:
for key, values in _star_star_dict(self.context, dct, el, func): for key, values in _star_star_dict(self.context, dct, el, funcdef):
yield key, values yield key, values
else: else:
if el.type == 'argument': if el.type == 'argument':
@@ -195,7 +195,7 @@ class ValuesArguments(AbstractArguments):
def __init__(self, values_list): def __init__(self, values_list):
self._values_list = values_list self._values_list = values_list
def unpack(self, func=None): def unpack(self, funcdef=None):
for values in self._values_list: for values in self._values_list:
yield None, context.LazyKnownContexts(values) yield None, context.LazyKnownContexts(values)
@@ -208,16 +208,16 @@ 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, var_args_context, original_param, var_args, lazy_context): def __init__(self, execution_context, param_node, lazy_context):
self._root_context = var_args_context.get_root_context() self._execution_context = execution_context
self._original_param = original_param self._param_node = param_node
self.var_args = var_args
self._lazy_context = lazy_context self._lazy_context = lazy_context
self.string_name = self._original_param.name.value self.string_name = param_node.name.value
def infer(self): def infer(self):
pep0484_hints = pep0484.infer_param(self._root_context, self._original_param) root_context = self._execution_context.get_root_context()
doc_params = docstrings.infer_param(self._root_context, self._original_param) pep0484_hints = pep0484.infer_param(root_context, self._param_node)
doc_params = docstrings.infer_param(root_context, self._param_node)
if pep0484_hints or doc_params: if pep0484_hints or doc_params:
return list(set(pep0484_hints) | set(doc_params)) return list(set(pep0484_hints) | set(doc_params))
@@ -226,25 +226,32 @@ class ExecutedParam(object):
@property @property
def position_nr(self): def position_nr(self):
# 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._param_node.position_nr
@property
def var_args(self):
return self._execution_context.var_args
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self.string_name) return '<%s: %s>' % (self.__class__.__name__, self.string_name)
def get_params(evaluator, parent_context, func, var_args): def get_params(execution_context, var_args):
result_params = [] result_params = []
param_dict = {} param_dict = {}
for param in func.params: funcdef = execution_context.tree_node
parent_context = execution_context.parent_context
for param in funcdef.params:
param_dict[param.name.value] = param param_dict[param.name.value] = param
unpacked_va = list(var_args.unpack(func)) unpacked_va = list(var_args.unpack(funcdef))
var_arg_iterator = common.PushBackIterator(iter(unpacked_va)) var_arg_iterator = common.PushBackIterator(iter(unpacked_va))
non_matching_keys = defaultdict(lambda: []) non_matching_keys = defaultdict(lambda: [])
keys_used = {} keys_used = {}
keys_only = False keys_only = False
had_multiple_value_error = False had_multiple_value_error = False
for param in func.params: for param in funcdef.params:
# The value and key can both be null. There, the defaults apply. # The value and key can both be null. There, the defaults apply.
# args / kwargs will just be empty arrays / dicts, respectively. # args / kwargs will just be empty arrays / dicts, respectively.
# Wrong value count is just ignored. If you try to test cases that are # Wrong value count is just ignored. If you try to test cases that are
@@ -260,12 +267,12 @@ def get_params(evaluator, parent_context, func, var_args):
if key in keys_used: if key in keys_used:
had_multiple_value_error = True had_multiple_value_error = True
m = ("TypeError: %s() got multiple values for keyword argument '%s'." m = ("TypeError: %s() got multiple values for keyword argument '%s'."
% (func.name, key)) % (funcdef.name, key))
for node in var_args.get_calling_nodes(): for node in var_args.get_calling_nodes():
analysis.add(parent_context, 'type-error-multiple-values', analysis.add(parent_context, 'type-error-multiple-values',
node, message=m) node, message=m)
else: else:
keys_used[key] = ExecutedParam(parent_context, key_param, var_args, argument) keys_used[key] = ExecutedParam(execution_context, key_param, argument)
key, argument = next(var_arg_iterator, (None, None)) key, argument = next(var_arg_iterator, (None, None))
try: try:
@@ -285,11 +292,11 @@ def get_params(evaluator, parent_context, func, var_args):
var_arg_iterator.push_back((key, argument)) var_arg_iterator.push_back((key, argument))
break break
lazy_context_list.append(argument) lazy_context_list.append(argument)
seq = iterable.FakeSequence(evaluator, 'tuple', lazy_context_list) seq = iterable.FakeSequence(execution_context.evaluator, 'tuple', lazy_context_list)
result_arg = context.LazyKnownContext(seq) result_arg = context.LazyKnownContext(seq)
elif param.star_count == 2: elif param.star_count == 2:
# **kwargs param # **kwargs param
dct = iterable.FakeDict(evaluator, dict(non_matching_keys)) dct = iterable.FakeDict(execution_context.evaluator, dict(non_matching_keys))
result_arg = context.LazyKnownContext(dct) result_arg = context.LazyKnownContext(dct)
non_matching_keys = {} non_matching_keys = {}
else: else:
@@ -300,7 +307,7 @@ def get_params(evaluator, parent_context, func, var_args):
result_arg = context.LazyUnknownContext() result_arg = context.LazyUnknownContext()
if not keys_only: if not keys_only:
for node in var_args.get_calling_nodes(): for node in var_args.get_calling_nodes():
m = _error_argument_count(func, len(unpacked_va)) m = _error_argument_count(funcdef, len(unpacked_va))
analysis.add(parent_context, 'type-error-too-few-arguments', analysis.add(parent_context, 'type-error-too-few-arguments',
node, message=m) node, message=m)
else: else:
@@ -308,7 +315,7 @@ def get_params(evaluator, parent_context, func, var_args):
else: else:
result_arg = argument result_arg = argument
result_params.append(ExecutedParam(parent_context, param, var_args, result_arg)) result_params.append(ExecutedParam(execution_context, param, result_arg))
if not isinstance(result_arg, context.LazyUnknownContext): if not isinstance(result_arg, context.LazyUnknownContext):
keys_used[param.name.value] = result_params[-1] keys_used[param.name.value] = result_params[-1]
@@ -323,13 +330,13 @@ def get_params(evaluator, parent_context, func, var_args):
param.star_count or param.default): param.star_count or param.default):
# add a warning only if there's not another one. # add a warning only if there's not another one.
for node in var_args.get_calling_nodes(): for node in var_args.get_calling_nodes():
m = _error_argument_count(func, len(unpacked_va)) m = _error_argument_count(funcdef, len(unpacked_va))
analysis.add(parent_context, 'type-error-too-few-arguments', analysis.add(parent_context, 'type-error-too-few-arguments',
node, message=m) node, message=m)
for key, lazy_context in non_matching_keys.items(): for key, lazy_context in non_matching_keys.items():
m = "TypeError: %s() got an unexpected keyword argument '%s'." \ m = "TypeError: %s() got an unexpected keyword argument '%s'." \
% (func.name, key) % (funcdef.name, key)
add_argument_issue( add_argument_issue(
parent_context, parent_context,
'type-error-keyword-argument', 'type-error-keyword-argument',
@@ -339,7 +346,7 @@ def get_params(evaluator, parent_context, func, var_args):
remaining_arguments = list(var_arg_iterator) remaining_arguments = list(var_arg_iterator)
if remaining_arguments: if remaining_arguments:
m = _error_argument_count(func, len(unpacked_va)) m = _error_argument_count(funcdef, len(unpacked_va))
# Just report an error for the first param that is not needed (like # Just report an error for the first param that is not needed (like
# cPython). # cPython).
first_key, lazy_context = remaining_arguments[0] first_key, lazy_context = remaining_arguments[0]
@@ -349,21 +356,21 @@ def get_params(evaluator, parent_context, func, var_args):
return result_params return result_params
def _iterate_star_args(context, array, input_node, func=None): def _iterate_star_args(context, array, input_node, funcdef=None):
try: try:
iter_ = array.py__iter__ iter_ = array.py__iter__
except AttributeError: except AttributeError:
if func is not None: if funcdef is not None:
# TODO this func should not be needed. # TODO this funcdef should not be needed.
m = "TypeError: %s() argument after * must be a sequence, not %s" \ m = "TypeError: %s() argument after * must be a sequence, not %s" \
% (func.name.value, array) % (funcdef.name.value, array)
analysis.add(context, 'type-error-star', input_node, message=m) analysis.add(context, 'type-error-star', input_node, message=m)
else: else:
for lazy_context in iter_(): for lazy_context in iter_():
yield lazy_context yield lazy_context
def _star_star_dict(context, array, input_node, func): def _star_star_dict(context, array, input_node, funcdef):
from jedi.evaluate.instance import CompiledInstance from jedi.evaluate.instance import CompiledInstance
if isinstance(array, CompiledInstance) and array.name.string_name == 'dict': if isinstance(array, CompiledInstance) and array.name.string_name == 'dict':
# For now ignore this case. In the future add proper iterators and just # For now ignore this case. In the future add proper iterators and just
@@ -372,35 +379,35 @@ def _star_star_dict(context, array, input_node, func):
elif isinstance(array, iterable.AbstractSequence) and array.array_type == 'dict': elif isinstance(array, iterable.AbstractSequence) and array.array_type == 'dict':
return array.exact_key_items() return array.exact_key_items()
else: else:
if func is not None: if funcdef is not None:
m = "TypeError: %s argument after ** must be a mapping, not %s" \ m = "TypeError: %s argument after ** must be a mapping, not %s" \
% (func.name.value, array) % (funcdef.name.value, array)
analysis.add(context, 'type-error-star-star', input_node, message=m) analysis.add(context, 'type-error-star-star', input_node, message=m)
return {} return {}
def _error_argument_count(func, actual_count): def _error_argument_count(funcdef, actual_count):
default_arguments = sum(1 for p in func.params if p.default or p.star_count) default_arguments = sum(1 for p in funcdef.params if p.default or p.star_count)
if default_arguments == 0: if default_arguments == 0:
before = 'exactly ' before = 'exactly '
else: else:
before = 'from %s to ' % (len(func.params) - default_arguments) before = 'from %s to ' % (len(funcdef.params) - default_arguments)
return ('TypeError: %s() takes %s%s arguments (%s given).' return ('TypeError: %s() takes %s%s arguments (%s given).'
% (func.name, before, len(func.params), actual_count)) % (funcdef.name, before, len(funcdef.params), actual_count))
def create_default_param(parent_context, param): def create_default_param(execution_context, param):
if param.star_count == 1: if param.star_count == 1:
result_arg = context.LazyKnownContext( result_arg = context.LazyKnownContext(
iterable.FakeSequence(parent_context.evaluator, 'tuple', []) iterable.FakeSequence(execution_context.evaluator, 'tuple', [])
) )
elif param.star_count == 2: elif param.star_count == 2:
result_arg = context.LazyKnownContext( result_arg = context.LazyKnownContext(
iterable.FakeDict(parent_context.evaluator, {}) iterable.FakeDict(execution_context.evaluator, {})
) )
elif param.default is None: elif param.default is None:
result_arg = context.LazyUnknownContext() result_arg = context.LazyUnknownContext()
else: else:
result_arg = context.LazyTreeContext(parent_context, param.default) result_arg = context.LazyTreeContext(execution_context.parent_context, param.default)
return ExecutedParam(parent_context, param, None, result_arg) return ExecutedParam(execution_context, param, result_arg)

View File

@@ -412,7 +412,7 @@ class FunctionExecutionContext(context.TreeContext):
@memoize_default(default=NO_DEFAULT) @memoize_default(default=NO_DEFAULT)
def get_params(self): def get_params(self):
return param.get_params(self.evaluator, self.parent_context, self.tree_node, self.var_args) return param.get_params(self, self.var_args)
class AnonymousFunctionExecution(FunctionExecutionContext): class AnonymousFunctionExecution(FunctionExecutionContext):
@@ -423,7 +423,7 @@ class AnonymousFunctionExecution(FunctionExecutionContext):
@memoize_default(default=NO_DEFAULT) @memoize_default(default=NO_DEFAULT)
def get_params(self): def get_params(self):
# We need to do a dynamic search here. # We need to do a dynamic search here.
return search_params(self.evaluator, self.parent_context, self.tree_node) return search_params(self.evaluator, self, self.tree_node)
class ModuleAttributeName(AbstractNameDefinition): class ModuleAttributeName(AbstractNameDefinition):