forked from VimPlug/jedi
Replace the get_iterator_types function with a different interface, which enables Jedi to detect invalid for loop inputs that are not iterable.
This commit is contained in:
@@ -300,13 +300,8 @@ class NameFinder(object):
|
||||
def _name_to_types(evaluator, name, scope):
|
||||
types = []
|
||||
typ = name.get_definition()
|
||||
if typ.isinstance(tree.ForStmt):
|
||||
for_types = evaluator.eval_element(typ.children[3])
|
||||
for_types = iterable.get_iterator_types(for_types)
|
||||
types += check_tuple_assignments(for_types, name)
|
||||
elif typ.isinstance(tree.CompFor):
|
||||
for_types = evaluator.eval_element(typ.children[3])
|
||||
for_types = iterable.get_iterator_types(for_types)
|
||||
if typ.isinstance(tree.ForStmt, tree.CompFor):
|
||||
for_types = iterable.get_iterator_types(evaluator, typ.children[3])
|
||||
types += check_tuple_assignments(for_types, name)
|
||||
elif isinstance(typ, tree.Param):
|
||||
types += _eval_param(evaluator, typ, scope)
|
||||
|
||||
@@ -379,17 +379,17 @@ class MergedArray(_FakeArray):
|
||||
return sum(len(a) for a in self._arrays)
|
||||
|
||||
|
||||
def get_iterator_types(inputs):
|
||||
def get_iterator_types(evaluator, element):
|
||||
"""Returns the types of any iterator (arrays, yields, __iter__, etc)."""
|
||||
iterators = []
|
||||
# Take the first statement (for has always only
|
||||
# one, remember `in`). And follow it.
|
||||
for it in inputs:
|
||||
for it in evaluator.eval_element(element):
|
||||
if isinstance(it, (Generator, Array, ArrayInstance, Comprehension)):
|
||||
iterators.append(it)
|
||||
else:
|
||||
if not hasattr(it, 'execute_subscope_by_name'):
|
||||
analysis.add(self._evaluator, 'type-error-generator', index_array)
|
||||
analysis.add(evaluator, 'type-error-not-iterable', element)
|
||||
debug.warning('iterator/for loop input wrong: %s', it)
|
||||
continue
|
||||
try:
|
||||
@@ -456,8 +456,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
||||
result += unite(evaluator.eval_element(node) for node in nodes)
|
||||
elif add_name in ['extend', 'update']:
|
||||
for key, nodes in params:
|
||||
iterators = unite(evaluator.eval_element(node) for node in nodes)
|
||||
result += get_iterator_types(iterators)
|
||||
result += unite(get_iterator_types(evaluator, node) for node in nodes)
|
||||
return result
|
||||
|
||||
from jedi.evaluate import representation as er, param
|
||||
@@ -568,8 +567,7 @@ class ArrayInstance(IterableWrapper):
|
||||
items = []
|
||||
for key, nodes in self.var_args.unpack():
|
||||
for node in nodes:
|
||||
for typ in self._evaluator.eval_element(node):
|
||||
items += get_iterator_types([typ])
|
||||
items += get_iterator_types(self._evaluator, node)
|
||||
|
||||
module = self.var_args.get_parent_until()
|
||||
is_list = str(self.instance.name) == 'list'
|
||||
|
||||
@@ -148,12 +148,16 @@ def builtins_super(evaluator, types, objects, scope):
|
||||
return []
|
||||
|
||||
|
||||
def get_iterable_content(evaluator, arguments, argument_index):
|
||||
nodes = list(arguments.unpack())[argument_index][1]
|
||||
return tuple(iterable.unite(iterable.get_iterator_types(node)
|
||||
for node in nodes))
|
||||
|
||||
|
||||
@argument_clinic('sequence, /', want_obj=True, want_arguments=True)
|
||||
def builtins_reversed(evaluator, sequences, obj, arguments):
|
||||
# Unpack the iterator values
|
||||
# TODO replace get_iterator_types.
|
||||
#elements = list(arguments.unpack())[0][1]
|
||||
objects = tuple(iterable.get_iterator_types(sequences))
|
||||
objects = get_iterable_content(evaluator, arguments, 0)
|
||||
rev = [iterable.AlreadyEvaluated([o]) for o in reversed(objects)]
|
||||
# Repack iterator values and then run it the normal way. This is
|
||||
# necessary, because `reversed` is a function and autocompletion
|
||||
@@ -165,8 +169,8 @@ def builtins_reversed(evaluator, sequences, obj, arguments):
|
||||
return [er.Instance(evaluator, obj, param.Arguments(evaluator, [rev]))]
|
||||
|
||||
|
||||
@argument_clinic('obj, type, /')
|
||||
def builtins_isinstance(evaluator, objects, types):
|
||||
@argument_clinic('obj, type, /', want_arguments=True)
|
||||
def builtins_isinstance(evaluator, objects, types, arguments):
|
||||
bool_results = set([])
|
||||
for o in objects:
|
||||
try:
|
||||
@@ -184,7 +188,7 @@ def builtins_isinstance(evaluator, objects, types):
|
||||
bool_results.add(cls_or_tup in mro)
|
||||
else:
|
||||
# Check for tuples.
|
||||
classes = iterable.get_iterator_types([cls_or_tup])
|
||||
classes = get_iterable_content(evaluator, arguments, 1)
|
||||
bool_results.add(any(cls in mro for cls in classes))
|
||||
|
||||
return [compiled.keyword_from_value(x) for x in bool_results]
|
||||
|
||||
@@ -92,7 +92,7 @@ def _paths_from_assignment(evaluator, expr_stmt):
|
||||
|
||||
from jedi.evaluate.iterable import get_iterator_types
|
||||
from jedi.evaluate.precedence import is_string
|
||||
for val in get_iterator_types(evaluator.eval_statement(expr_stmt)):
|
||||
for val in get_iterator_types(evaluator, expr_stmt):
|
||||
if is_string(val):
|
||||
yield val.obj
|
||||
|
||||
|
||||
Reference in New Issue
Block a user