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))
|
result.append(er.Function(self, call))
|
||||||
# With things like params, these can also be functions...
|
# With things like params, these can also be functions...
|
||||||
elif isinstance(call, pr.Base) and call.isinstance(
|
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)
|
result.append(call)
|
||||||
# The string tokens are just operations (+, -, etc.)
|
# The string tokens are just operations (+, -, etc.)
|
||||||
elif not isinstance(call, (str, unicode)):
|
elif not isinstance(call, (str, unicode)):
|
||||||
@@ -764,7 +764,7 @@ def get_iterator_types(inputs):
|
|||||||
# Take the first statement (for has always only
|
# Take the first statement (for has always only
|
||||||
# one, remember `in`). And follow it.
|
# one, remember `in`). And follow it.
|
||||||
for it in inputs:
|
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)
|
iterators.append(it)
|
||||||
else:
|
else:
|
||||||
if not hasattr(it, 'execute_subscope_by_name'):
|
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.parser import representation as pr
|
||||||
from jedi import modules
|
from jedi import modules
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi import debug
|
|
||||||
from jedi.parser import fast as fast_parser
|
from jedi.parser import fast as fast_parser
|
||||||
from jedi.evaluate.cache import memoize_default
|
from jedi.evaluate.cache import memoize_default
|
||||||
|
|
||||||
@@ -232,18 +231,6 @@ def search_params(evaluator, param):
|
|||||||
return result
|
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):
|
def _scan_statement(stmt, search_name, assignment_details=False):
|
||||||
""" Returns the function Call that match search_name in an Array. """
|
""" Returns the function Call that match search_name in an Array. """
|
||||||
def scan_array(arr, search_name):
|
def scan_array(arr, search_name):
|
||||||
@@ -280,163 +267,6 @@ def _scan_statement(stmt, search_name, assignment_details=False):
|
|||||||
return result
|
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):
|
def check_flow_information(evaluator, flow, search_name, pos):
|
||||||
""" Try to find out the type of a variable just with the information that
|
""" 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.::
|
is given by the flows: e.g. It is also responsible for assert checks.::
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from jedi._compatibility import use_metaclass
|
|
||||||
from jedi import common
|
from jedi import common
|
||||||
from jedi.parser import representation as pr
|
|
||||||
from jedi import debug
|
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 builtin
|
||||||
from jedi.evaluate import dynamic
|
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)):
|
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])
|
return self.get_exact_index_types(index.var_args[0])
|
||||||
|
|
||||||
result = list(self._follow_values(self._array.values))
|
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)
|
return set(result)
|
||||||
|
|
||||||
def get_exact_index_types(self, mixed_index):
|
def get_exact_index_types(self, mixed_index):
|
||||||
@@ -180,3 +181,172 @@ class ArrayMethod(object):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s of %s>" % (type(self).__name__, self.name)
|
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 builtin
|
||||||
from jedi.evaluate import recursion
|
from jedi.evaluate import recursion
|
||||||
from jedi.evaluate.cache import memoize_default, CachedMetaClass
|
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 import docstrings
|
||||||
from jedi.evaluate import dynamic
|
|
||||||
|
|
||||||
|
|
||||||
class Executable(pr.IsScope):
|
class Executable(pr.IsScope):
|
||||||
@@ -51,7 +50,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
|
|||||||
if str(base.name) in ['list', 'set'] \
|
if str(base.name) in ['list', 'set'] \
|
||||||
and builtin.Builtin.scope == base.get_parent_until():
|
and builtin.Builtin.scope == base.get_parent_until():
|
||||||
# compare the module path with the builtin name.
|
# 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:
|
else:
|
||||||
# need to execute the __init__ function, because the dynamic param
|
# need to execute the __init__ function, because the dynamic param
|
||||||
# searching needs it.
|
# searching needs it.
|
||||||
@@ -406,7 +405,7 @@ class FunctionExecution(Executable):
|
|||||||
for listener in func.listeners:
|
for listener in func.listeners:
|
||||||
listener.execute(self._get_params())
|
listener.execute(self._get_params())
|
||||||
if func.is_generator and not evaluate_generator:
|
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:
|
else:
|
||||||
stmts = docstrings.find_return_types(self._evaluator, func)
|
stmts = docstrings.find_return_types(self._evaluator, func)
|
||||||
for r in self.returns:
|
for r in self.returns:
|
||||||
@@ -570,17 +569,17 @@ class FunctionExecution(Executable):
|
|||||||
# *args must be some sort of an array, otherwise -> ignore
|
# *args must be some sort of an array, otherwise -> ignore
|
||||||
|
|
||||||
for array in arrays:
|
for array in arrays:
|
||||||
if isinstance(array, Array):
|
if isinstance(array, iterable.Array):
|
||||||
for field_stmt in array: # yield from plz!
|
for field_stmt in array: # yield from plz!
|
||||||
yield None, field_stmt
|
yield None, field_stmt
|
||||||
elif isinstance(array, Generator):
|
elif isinstance(array, iterable.Generator):
|
||||||
for field_stmt in array.iter_content():
|
for field_stmt in array.iter_content():
|
||||||
yield None, helpers.FakeStatement(field_stmt)
|
yield None, helpers.FakeStatement(field_stmt)
|
||||||
# **kwargs
|
# **kwargs
|
||||||
elif expression_list[0] == '**':
|
elif expression_list[0] == '**':
|
||||||
arrays = self._evaluator.eval_expression_list(expression_list[1:])
|
arrays = self._evaluator.eval_expression_list(expression_list[1:])
|
||||||
for array in arrays:
|
for array in arrays:
|
||||||
if isinstance(array, Array):
|
if isinstance(array, iterable.Array):
|
||||||
for key_stmt, value_stmt in array.items():
|
for key_stmt, value_stmt in array.items():
|
||||||
# first index, is the key if syntactically correct
|
# first index, is the key if syntactically correct
|
||||||
call = key_stmt.expression_list()[0]
|
call = key_stmt.expression_list()[0]
|
||||||
|
|||||||
Reference in New Issue
Block a user