forked from VimPlug/jedi
move dynamic array stuff to evaluate.iterable
This commit is contained in:
@@ -547,7 +547,7 @@ class Evaluator(object):
|
||||
result.append(er.Function(self, call))
|
||||
# With things like params, these can also be functions...
|
||||
elif isinstance(call, pr.Base) and call.isinstance(
|
||||
er.Function, er.Class, er.Instance, dynamic.ArrayInstance):
|
||||
er.Function, er.Class, er.Instance, iterable.ArrayInstance):
|
||||
result.append(call)
|
||||
# The string tokens are just operations (+, -, etc.)
|
||||
elif not isinstance(call, (str, unicode)):
|
||||
@@ -764,7 +764,7 @@ def get_iterator_types(inputs):
|
||||
# Take the first statement (for has always only
|
||||
# one, remember `in`). And follow it.
|
||||
for it in inputs:
|
||||
if isinstance(it, (iterable.Generator, iterable.Array, dynamic.ArrayInstance)):
|
||||
if isinstance(it, (iterable.Generator, iterable.Array, iterable.ArrayInstance)):
|
||||
iterators.append(it)
|
||||
else:
|
||||
if not hasattr(it, 'execute_subscope_by_name'):
|
||||
|
||||
@@ -57,7 +57,6 @@ from jedi import cache
|
||||
from jedi.parser import representation as pr
|
||||
from jedi import modules
|
||||
from jedi import settings
|
||||
from jedi import debug
|
||||
from jedi.parser import fast as fast_parser
|
||||
from jedi.evaluate.cache import memoize_default
|
||||
|
||||
@@ -232,18 +231,6 @@ def search_params(evaluator, param):
|
||||
return result
|
||||
|
||||
|
||||
def check_array_additions(evaluator, array):
|
||||
""" Just a mapper function for the internal _check_array_additions """
|
||||
if not pr.Array.is_type(array._array, pr.Array.LIST, pr.Array.SET):
|
||||
# TODO also check for dict updates
|
||||
return []
|
||||
|
||||
is_list = array._array.type == 'list'
|
||||
current_module = array._array.get_parent_until()
|
||||
res = _check_array_additions(evaluator, array, current_module, is_list)
|
||||
return res
|
||||
|
||||
|
||||
def _scan_statement(stmt, search_name, assignment_details=False):
|
||||
""" Returns the function Call that match search_name in an Array. """
|
||||
def scan_array(arr, search_name):
|
||||
@@ -280,163 +267,6 @@ def _scan_statement(stmt, search_name, assignment_details=False):
|
||||
return result
|
||||
|
||||
|
||||
@memoize_default([], evaluator_is_first_arg=True)
|
||||
def _check_array_additions(evaluator, compare_array, module, is_list):
|
||||
"""
|
||||
Checks if a `pr.Array` has "add" statements:
|
||||
>>> a = [""]
|
||||
>>> a.append(1)
|
||||
"""
|
||||
if not settings.dynamic_array_additions or module.is_builtin():
|
||||
return []
|
||||
|
||||
def check_calls(calls, add_name):
|
||||
"""
|
||||
Calls are processed here. The part before the call is searched and
|
||||
compared with the original Array.
|
||||
"""
|
||||
result = []
|
||||
for c in calls:
|
||||
call_path = list(c.generate_call_path())
|
||||
separate_index = call_path.index(add_name)
|
||||
if add_name == call_path[-1] or separate_index == 0:
|
||||
# this means that there is no execution -> [].append
|
||||
# or the keyword is at the start -> append()
|
||||
continue
|
||||
backtrack_path = iter(call_path[:separate_index])
|
||||
|
||||
position = c.start_pos
|
||||
scope = c.get_parent_until(pr.IsScope)
|
||||
|
||||
found = evaluator.eval_call_path(backtrack_path, scope, position)
|
||||
if not compare_array in found:
|
||||
continue
|
||||
|
||||
params = call_path[separate_index + 1]
|
||||
if not params.values:
|
||||
continue # no params: just ignore it
|
||||
if add_name in ['append', 'add']:
|
||||
for param in params:
|
||||
result += evaluator.eval_statement(param)
|
||||
elif add_name in ['insert']:
|
||||
try:
|
||||
second_param = params[1]
|
||||
except IndexError:
|
||||
continue
|
||||
else:
|
||||
result += evaluator.eval_statement(second_param)
|
||||
elif add_name in ['extend', 'update']:
|
||||
for param in params:
|
||||
iterators = evaluator.eval_statement(param)
|
||||
result += evaluate.get_iterator_types(iterators)
|
||||
return result
|
||||
|
||||
from jedi.evaluate import representation as er
|
||||
from jedi import evaluate
|
||||
from jedi.evaluate import iterable
|
||||
|
||||
def get_execution_parent(element, *stop_classes):
|
||||
""" Used to get an Instance/FunctionExecution parent """
|
||||
if isinstance(element, iterable.Array):
|
||||
stmt = element._array.parent
|
||||
else:
|
||||
# is an Instance with an ArrayInstance inside
|
||||
stmt = element.var_args[0].var_args.parent
|
||||
if isinstance(stmt, er.InstanceElement):
|
||||
stop_classes = list(stop_classes) + [er.Function]
|
||||
return stmt.get_parent_until(stop_classes)
|
||||
|
||||
temp_param_add = settings.dynamic_params_for_other_modules
|
||||
settings.dynamic_params_for_other_modules = False
|
||||
|
||||
search_names = ['append', 'extend', 'insert'] if is_list else \
|
||||
['add', 'update']
|
||||
comp_arr_parent = get_execution_parent(compare_array, er.FunctionExecution)
|
||||
|
||||
possible_stmts = []
|
||||
res = []
|
||||
for n in search_names:
|
||||
try:
|
||||
possible_stmts += module.used_names[n]
|
||||
except KeyError:
|
||||
continue
|
||||
for stmt in possible_stmts:
|
||||
# Check if the original scope is an execution. If it is, one
|
||||
# can search for the same statement, that is in the module
|
||||
# dict. Executions are somewhat special in jedi, since they
|
||||
# literally copy the contents of a function.
|
||||
if isinstance(comp_arr_parent, er.FunctionExecution):
|
||||
stmt = comp_arr_parent. \
|
||||
get_statement_for_position(stmt.start_pos)
|
||||
if stmt is None:
|
||||
continue
|
||||
# InstanceElements are special, because they don't get copied,
|
||||
# but have this wrapper around them.
|
||||
if isinstance(comp_arr_parent, er.InstanceElement):
|
||||
stmt = er.InstanceElement(comp_arr_parent.instance, stmt)
|
||||
|
||||
if evaluator.recursion_detector.push_stmt(stmt):
|
||||
# check recursion
|
||||
continue
|
||||
res += check_calls(_scan_statement(stmt, n), n)
|
||||
evaluator.recursion_detector.pop_stmt()
|
||||
# reset settings
|
||||
settings.dynamic_params_for_other_modules = temp_param_add
|
||||
return res
|
||||
|
||||
|
||||
def check_array_instances(evaluator, instance):
|
||||
"""Used for set() and list() instances."""
|
||||
if not settings.dynamic_arrays_instances:
|
||||
return instance.var_args
|
||||
ai = ArrayInstance(evaluator, instance)
|
||||
return [ai]
|
||||
|
||||
|
||||
class ArrayInstance(pr.Base):
|
||||
"""
|
||||
Used for the usage of set() and list().
|
||||
This is definitely a hack, but a good one :-)
|
||||
It makes it possible to use set/list conversions.
|
||||
"""
|
||||
def __init__(self, evaluator, instance):
|
||||
self._evaluator = evaluator
|
||||
self.instance = instance
|
||||
self.var_args = instance.var_args
|
||||
|
||||
def iter_content(self):
|
||||
"""
|
||||
The index is here just ignored, because of all the appends, etc.
|
||||
lists/sets are too complicated too handle that.
|
||||
"""
|
||||
items = []
|
||||
from jedi import evaluate
|
||||
for stmt in self.var_args:
|
||||
for typ in self._evaluator.eval_statement(stmt):
|
||||
if isinstance(typ, evaluate.er.Instance) and len(typ.var_args):
|
||||
array = typ.var_args[0]
|
||||
if isinstance(array, ArrayInstance):
|
||||
# prevent recursions
|
||||
# TODO compare Modules
|
||||
if self.var_args.start_pos != array.var_args.start_pos:
|
||||
items += array.iter_content()
|
||||
else:
|
||||
debug.warning(
|
||||
'ArrayInstance recursion',
|
||||
self.var_args)
|
||||
continue
|
||||
items += evaluate.get_iterator_types([typ])
|
||||
|
||||
# TODO check if exclusion of tuple is a problem here.
|
||||
if isinstance(self.var_args, tuple) or self.var_args.parent is None:
|
||||
return [] # generated var_args should not be checked for arrays
|
||||
|
||||
module = self.var_args.get_parent_until()
|
||||
is_list = str(self.instance.name) == 'list'
|
||||
items += _check_array_additions(self._evaluator, self.instance, module, is_list)
|
||||
return items
|
||||
|
||||
|
||||
def check_flow_information(evaluator, flow, search_name, pos):
|
||||
""" Try to find out the type of a variable just with the information that
|
||||
is given by the flows: e.g. It is also responsible for assert checks.::
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import itertools
|
||||
|
||||
from jedi._compatibility import use_metaclass
|
||||
from jedi import common
|
||||
from jedi.parser import representation as pr
|
||||
from jedi import debug
|
||||
from jedi import settings
|
||||
from jedi._compatibility import use_metaclass
|
||||
from jedi.parser import representation as pr
|
||||
from jedi.evaluate import builtin
|
||||
from jedi.evaluate import dynamic
|
||||
from jedi.evaluate.cache import CachedMetaClass
|
||||
from jedi.evaluate.cache import CachedMetaClass, memoize_default
|
||||
|
||||
|
||||
class Generator(use_metaclass(CachedMetaClass, pr.Base)):
|
||||
@@ -89,7 +90,7 @@ class Array(use_metaclass(CachedMetaClass, pr.Base)):
|
||||
return self.get_exact_index_types(index.var_args[0])
|
||||
|
||||
result = list(self._follow_values(self._array.values))
|
||||
result += dynamic.check_array_additions(self._evaluator, self)
|
||||
result += check_array_additions(self._evaluator, self)
|
||||
return set(result)
|
||||
|
||||
def get_exact_index_types(self, mixed_index):
|
||||
@@ -180,3 +181,172 @@ class ArrayMethod(object):
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s of %s>" % (type(self).__name__, self.name)
|
||||
|
||||
|
||||
def check_array_additions(evaluator, array):
|
||||
""" Just a mapper function for the internal _check_array_additions """
|
||||
if not pr.Array.is_type(array._array, pr.Array.LIST, pr.Array.SET):
|
||||
# TODO also check for dict updates
|
||||
return []
|
||||
|
||||
is_list = array._array.type == 'list'
|
||||
current_module = array._array.get_parent_until()
|
||||
res = _check_array_additions(evaluator, array, current_module, is_list)
|
||||
return res
|
||||
|
||||
|
||||
@memoize_default([], evaluator_is_first_arg=True)
|
||||
def _check_array_additions(evaluator, compare_array, module, is_list):
|
||||
"""
|
||||
Checks if a `pr.Array` has "add" statements:
|
||||
>>> a = [""]
|
||||
>>> a.append(1)
|
||||
"""
|
||||
if not settings.dynamic_array_additions or module.is_builtin():
|
||||
return []
|
||||
|
||||
def check_calls(calls, add_name):
|
||||
"""
|
||||
Calls are processed here. The part before the call is searched and
|
||||
compared with the original Array.
|
||||
"""
|
||||
result = []
|
||||
for c in calls:
|
||||
call_path = list(c.generate_call_path())
|
||||
separate_index = call_path.index(add_name)
|
||||
if add_name == call_path[-1] or separate_index == 0:
|
||||
# this means that there is no execution -> [].append
|
||||
# or the keyword is at the start -> append()
|
||||
continue
|
||||
backtrack_path = iter(call_path[:separate_index])
|
||||
|
||||
position = c.start_pos
|
||||
scope = c.get_parent_until(pr.IsScope)
|
||||
|
||||
found = evaluator.eval_call_path(backtrack_path, scope, position)
|
||||
if not compare_array in found:
|
||||
continue
|
||||
|
||||
params = call_path[separate_index + 1]
|
||||
if not params.values:
|
||||
continue # no params: just ignore it
|
||||
if add_name in ['append', 'add']:
|
||||
for param in params:
|
||||
result += evaluator.eval_statement(param)
|
||||
elif add_name in ['insert']:
|
||||
try:
|
||||
second_param = params[1]
|
||||
except IndexError:
|
||||
continue
|
||||
else:
|
||||
result += evaluator.eval_statement(second_param)
|
||||
elif add_name in ['extend', 'update']:
|
||||
for param in params:
|
||||
iterators = evaluator.eval_statement(param)
|
||||
result += evaluate.get_iterator_types(iterators)
|
||||
return result
|
||||
|
||||
from jedi.evaluate import representation as er
|
||||
from jedi import evaluate
|
||||
from jedi.evaluate import iterable
|
||||
|
||||
def get_execution_parent(element, *stop_classes):
|
||||
""" Used to get an Instance/FunctionExecution parent """
|
||||
if isinstance(element, iterable.Array):
|
||||
stmt = element._array.parent
|
||||
else:
|
||||
# is an Instance with an ArrayInstance inside
|
||||
stmt = element.var_args[0].var_args.parent
|
||||
if isinstance(stmt, er.InstanceElement):
|
||||
stop_classes = list(stop_classes) + [er.Function]
|
||||
return stmt.get_parent_until(stop_classes)
|
||||
|
||||
temp_param_add = settings.dynamic_params_for_other_modules
|
||||
settings.dynamic_params_for_other_modules = False
|
||||
|
||||
search_names = ['append', 'extend', 'insert'] if is_list else \
|
||||
['add', 'update']
|
||||
comp_arr_parent = get_execution_parent(compare_array, er.FunctionExecution)
|
||||
|
||||
possible_stmts = []
|
||||
res = []
|
||||
for n in search_names:
|
||||
try:
|
||||
possible_stmts += module.used_names[n]
|
||||
except KeyError:
|
||||
continue
|
||||
for stmt in possible_stmts:
|
||||
# Check if the original scope is an execution. If it is, one
|
||||
# can search for the same statement, that is in the module
|
||||
# dict. Executions are somewhat special in jedi, since they
|
||||
# literally copy the contents of a function.
|
||||
if isinstance(comp_arr_parent, er.FunctionExecution):
|
||||
stmt = comp_arr_parent. \
|
||||
get_statement_for_position(stmt.start_pos)
|
||||
if stmt is None:
|
||||
continue
|
||||
# InstanceElements are special, because they don't get copied,
|
||||
# but have this wrapper around them.
|
||||
if isinstance(comp_arr_parent, er.InstanceElement):
|
||||
stmt = er.InstanceElement(comp_arr_parent.instance, stmt)
|
||||
|
||||
if evaluator.recursion_detector.push_stmt(stmt):
|
||||
# check recursion
|
||||
continue
|
||||
res += check_calls(dynamic._scan_statement(stmt, n), n)
|
||||
evaluator.recursion_detector.pop_stmt()
|
||||
# reset settings
|
||||
settings.dynamic_params_for_other_modules = temp_param_add
|
||||
return res
|
||||
|
||||
|
||||
def check_array_instances(evaluator, instance):
|
||||
"""Used for set() and list() instances."""
|
||||
if not settings.dynamic_arrays_instances:
|
||||
return instance.var_args
|
||||
ai = ArrayInstance(evaluator, instance)
|
||||
return [ai]
|
||||
|
||||
|
||||
class ArrayInstance(pr.Base):
|
||||
"""
|
||||
Used for the usage of set() and list().
|
||||
This is definitely a hack, but a good one :-)
|
||||
It makes it possible to use set/list conversions.
|
||||
"""
|
||||
def __init__(self, evaluator, instance):
|
||||
self._evaluator = evaluator
|
||||
self.instance = instance
|
||||
self.var_args = instance.var_args
|
||||
|
||||
def iter_content(self):
|
||||
"""
|
||||
The index is here just ignored, because of all the appends, etc.
|
||||
lists/sets are too complicated too handle that.
|
||||
"""
|
||||
items = []
|
||||
from jedi import evaluate
|
||||
for stmt in self.var_args:
|
||||
for typ in self._evaluator.eval_statement(stmt):
|
||||
if isinstance(typ, evaluate.er.Instance) and len(typ.var_args):
|
||||
array = typ.var_args[0]
|
||||
if isinstance(array, ArrayInstance):
|
||||
# prevent recursions
|
||||
# TODO compare Modules
|
||||
if self.var_args.start_pos != array.var_args.start_pos:
|
||||
items += array.iter_content()
|
||||
else:
|
||||
debug.warning(
|
||||
'ArrayInstance recursion',
|
||||
self.var_args)
|
||||
continue
|
||||
items += evaluate.get_iterator_types([typ])
|
||||
|
||||
# TODO check if exclusion of tuple is a problem here.
|
||||
if isinstance(self.var_args, tuple) or self.var_args.parent is None:
|
||||
return [] # generated var_args should not be checked for arrays
|
||||
|
||||
module = self.var_args.get_parent_until()
|
||||
is_list = str(self.instance.name) == 'list'
|
||||
items += _check_array_additions(self._evaluator, self.instance, module, is_list)
|
||||
return items
|
||||
|
||||
@@ -19,9 +19,8 @@ from jedi import common
|
||||
from jedi.evaluate import builtin
|
||||
from jedi.evaluate import recursion
|
||||
from jedi.evaluate.cache import memoize_default, CachedMetaClass
|
||||
from jedi.evaluate.iterable import Array, Generator
|
||||
from jedi.evaluate import iterable
|
||||
from jedi import docstrings
|
||||
from jedi.evaluate import dynamic
|
||||
|
||||
|
||||
class Executable(pr.IsScope):
|
||||
@@ -51,7 +50,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
|
||||
if str(base.name) in ['list', 'set'] \
|
||||
and builtin.Builtin.scope == base.get_parent_until():
|
||||
# compare the module path with the builtin name.
|
||||
self.var_args = dynamic.check_array_instances(evaluator, self)
|
||||
self.var_args = iterable.check_array_instances(evaluator, self)
|
||||
else:
|
||||
# need to execute the __init__ function, because the dynamic param
|
||||
# searching needs it.
|
||||
@@ -406,7 +405,7 @@ class FunctionExecution(Executable):
|
||||
for listener in func.listeners:
|
||||
listener.execute(self._get_params())
|
||||
if func.is_generator and not evaluate_generator:
|
||||
return [Generator(self._evaluator, func, self.var_args)]
|
||||
return [iterable.Generator(self._evaluator, func, self.var_args)]
|
||||
else:
|
||||
stmts = docstrings.find_return_types(self._evaluator, func)
|
||||
for r in self.returns:
|
||||
@@ -570,17 +569,17 @@ class FunctionExecution(Executable):
|
||||
# *args must be some sort of an array, otherwise -> ignore
|
||||
|
||||
for array in arrays:
|
||||
if isinstance(array, Array):
|
||||
if isinstance(array, iterable.Array):
|
||||
for field_stmt in array: # yield from plz!
|
||||
yield None, field_stmt
|
||||
elif isinstance(array, Generator):
|
||||
elif isinstance(array, iterable.Generator):
|
||||
for field_stmt in array.iter_content():
|
||||
yield None, helpers.FakeStatement(field_stmt)
|
||||
# **kwargs
|
||||
elif expression_list[0] == '**':
|
||||
arrays = self._evaluator.eval_expression_list(expression_list[1:])
|
||||
for array in arrays:
|
||||
if isinstance(array, Array):
|
||||
if isinstance(array, iterable.Array):
|
||||
for key_stmt, value_stmt in array.items():
|
||||
# first index, is the key if syntactically correct
|
||||
call = key_stmt.expression_list()[0]
|
||||
|
||||
Reference in New Issue
Block a user