mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-18 03:25:55 +08:00
Fix dynamic arrays.
This commit is contained in:
@@ -50,6 +50,9 @@ class Context(object):
|
|||||||
search_global=False, is_goto=False):
|
search_global=False, is_goto=False):
|
||||||
return self.evaluator.find_types(self, name_or_str, position, search_global, is_goto)
|
return self.evaluator.find_types(self, name_or_str, position, search_global, is_goto)
|
||||||
|
|
||||||
|
def create_context(self, node):
|
||||||
|
return self.evaluator.create_context(self, node)
|
||||||
|
|
||||||
|
|
||||||
class TreeContext(Context):
|
class TreeContext(Context):
|
||||||
def __init__(self, evaluator, parent_context=None):
|
def __init__(self, evaluator, parent_context=None):
|
||||||
|
|||||||
@@ -7,19 +7,16 @@ from jedi.evaluate import compiled
|
|||||||
from jedi.evaluate import filters
|
from jedi.evaluate import filters
|
||||||
from jedi.evaluate.context import Context, LazyKnownContext, LazyKnownContexts
|
from jedi.evaluate.context import Context, LazyKnownContext, LazyKnownContexts
|
||||||
from jedi.evaluate.cache import memoize_default
|
from jedi.evaluate.cache import memoize_default
|
||||||
from jedi.evaluate.param import ValuesArguments
|
|
||||||
from jedi.cache import memoize_method
|
from jedi.cache import memoize_method
|
||||||
from jedi.evaluate import representation as er
|
from jedi.evaluate import representation as er
|
||||||
from jedi.evaluate.dynamic import search_params
|
from jedi.evaluate.dynamic import search_params
|
||||||
|
from jedi.evaluate import iterable
|
||||||
|
|
||||||
|
|
||||||
class AbstractInstanceContext(Context):
|
class AbstractInstanceContext(Context):
|
||||||
"""
|
"""
|
||||||
This class is used to evaluate instances.
|
This class is used to evaluate instances.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_faked_class = None
|
|
||||||
|
|
||||||
def __init__(self, evaluator, parent_context, class_context, var_args):
|
def __init__(self, evaluator, parent_context, class_context, var_args):
|
||||||
super(AbstractInstanceContext, self).__init__(evaluator, parent_context)
|
super(AbstractInstanceContext, self).__init__(evaluator, parent_context)
|
||||||
# Generated instances are classes that are just generated by self
|
# Generated instances are classes that are just generated by self
|
||||||
@@ -27,23 +24,6 @@ class AbstractInstanceContext(Context):
|
|||||||
self.class_context = class_context
|
self.class_context = class_context
|
||||||
self.var_args = var_args
|
self.var_args = var_args
|
||||||
|
|
||||||
#####
|
|
||||||
""""
|
|
||||||
if class_context.name.string_name in ['list', 'set'] \
|
|
||||||
and evaluator.BUILTINS == parent_context.get_root_context():
|
|
||||||
# compare the module path with the builtin name.
|
|
||||||
self.var_args = iterable.check_array_instances(evaluator, self)
|
|
||||||
elif not is_generated:
|
|
||||||
# Need to execute the __init__ function, because the dynamic param
|
|
||||||
# searching needs it.
|
|
||||||
try:
|
|
||||||
method = self.get_subscope_by_name('__init__')
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self._init_execution = evaluator.execute(method, self.var_args)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def is_class(self):
|
def is_class(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -191,6 +171,16 @@ class AbstractInstanceContext(Context):
|
|||||||
|
|
||||||
|
|
||||||
class CompiledInstance(AbstractInstanceContext):
|
class CompiledInstance(AbstractInstanceContext):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CompiledInstance, self).__init__(*args, **kwargs)
|
||||||
|
# I don't think that dynamic append lookups should happen here. That
|
||||||
|
# sounds more like something that should go to py__iter__.
|
||||||
|
if self.class_context.name.string_name in ['list', 'set'] \
|
||||||
|
and self.parent_context.get_root_context() == self.evaluator.BUILTINS:
|
||||||
|
# compare the module path with the builtin name.
|
||||||
|
self.var_args = iterable.get_dynamic_array_instance(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return compiled.CompiledContextName(self, self.class_context.name.string_name)
|
return compiled.CompiledContextName(self, self.class_context.name.string_name)
|
||||||
|
|||||||
@@ -399,9 +399,8 @@ class ArrayLiteralContext(ArrayMixin, AbstractSequence):
|
|||||||
for node in self._items():
|
for node in self._items():
|
||||||
yield context.LazyTreeContext(self._defining_context, node)
|
yield context.LazyTreeContext(self._defining_context, node)
|
||||||
|
|
||||||
additions = check_array_additions(self.evaluator, self)
|
for addition in check_array_additions(self._defining_context, self):
|
||||||
if additions:
|
yield addition
|
||||||
yield additions
|
|
||||||
|
|
||||||
def _values(self):
|
def _values(self):
|
||||||
"""Returns a list of a list of node."""
|
"""Returns a list of a list of node."""
|
||||||
@@ -683,55 +682,46 @@ def py__getitem__(evaluator, context, types, trailer):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def check_array_additions(evaluator, array):
|
def check_array_additions(context, sequence):
|
||||||
""" Just a mapper function for the internal _check_array_additions """
|
""" Just a mapper function for the internal _check_array_additions """
|
||||||
if array.array_type not in ('list', 'set'):
|
if sequence.array_type not in ('list', 'set'):
|
||||||
# TODO also check for dict updates
|
# TODO also check for dict updates
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
return set()
|
return _check_array_additions(context, sequence)
|
||||||
is_list = array.array_type == 'list'
|
|
||||||
try:
|
|
||||||
current_module = array.atom.get_parent_until()
|
|
||||||
except AttributeError:
|
|
||||||
# If there's no get_parent_until, it's a FakeSequence or another Fake
|
|
||||||
# type. Those fake types are used inside Jedi's engine. No values may
|
|
||||||
# be added to those after their creation.
|
|
||||||
return set()
|
|
||||||
return _check_array_additions(evaluator, array, current_module, is_list)
|
|
||||||
|
|
||||||
|
|
||||||
@memoize_default(default=set(), evaluator_is_first_arg=True)
|
@memoize_default(default=set())
|
||||||
@debug.increase_indent
|
@debug.increase_indent
|
||||||
def _check_array_additions(evaluator, compare_array, module, is_list):
|
def _check_array_additions(context, sequence):
|
||||||
"""
|
"""
|
||||||
Checks if a `Array` has "add" (append, insert, extend) statements:
|
Checks if a `Array` has "add" (append, insert, extend) statements:
|
||||||
|
|
||||||
>>> a = [""]
|
>>> a = [""]
|
||||||
>>> a.append(1)
|
>>> a.append(1)
|
||||||
"""
|
"""
|
||||||
debug.dbg('Dynamic array search for %s' % compare_array, color='MAGENTA')
|
from jedi.evaluate import representation as er, param
|
||||||
if not settings.dynamic_array_additions or isinstance(module, compiled.CompiledObject):
|
|
||||||
|
debug.dbg('Dynamic array search for %s' % sequence, color='MAGENTA')
|
||||||
|
module_context = context.get_root_context()
|
||||||
|
if not settings.dynamic_array_additions or isinstance(module_context, compiled.CompiledObject):
|
||||||
debug.dbg('Dynamic array search aborted.', color='MAGENTA')
|
debug.dbg('Dynamic array search aborted.', color='MAGENTA')
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
def check_additions(arglist, add_name):
|
def find_additions(context, arglist, add_name):
|
||||||
params = list(param.Arguments(evaluator, context, arglist).unpack())
|
params = list(param.TreeArguments(context.evaluator, context, arglist).unpack())
|
||||||
result = set()
|
result = set()
|
||||||
if add_name in ['insert']:
|
if add_name in ['insert']:
|
||||||
params = params[1:]
|
params = params[1:]
|
||||||
if add_name in ['append', 'add', 'insert']:
|
if add_name in ['append', 'add', 'insert']:
|
||||||
for key, nodes in params:
|
for key, lazy_context in params:
|
||||||
result |= unite(evaluator.eval_element(node) for node in nodes)
|
result.add(lazy_context)
|
||||||
elif add_name in ['extend', 'update']:
|
elif add_name in ['extend', 'update']:
|
||||||
for key, nodes in params:
|
for key, lazy_context in params:
|
||||||
for node in nodes:
|
result |= set(py__iter__(context.evaluator, lazy_context.infer()))
|
||||||
types = evaluator.eval_element(node)
|
|
||||||
result |= py__iter__types(evaluator, types, node)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
from jedi.evaluate import representation as er, param
|
'''
|
||||||
|
|
||||||
def get_execution_parent(element):
|
def get_execution_parent(element):
|
||||||
""" Used to get an Instance/FunctionExecution parent """
|
""" Used to get an Instance/FunctionExecution parent """
|
||||||
if isinstance(element, Array):
|
if isinstance(element, Array):
|
||||||
@@ -744,21 +734,27 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
|||||||
if isinstance(node, er.InstanceElement) or node is None:
|
if isinstance(node, er.InstanceElement) or node is None:
|
||||||
return node
|
return node
|
||||||
return node.get_parent_until(er.FunctionExecution)
|
return node.get_parent_until(er.FunctionExecution)
|
||||||
|
'''
|
||||||
|
|
||||||
temp_param_add, settings.dynamic_params_for_other_modules = \
|
temp_param_add, settings.dynamic_params_for_other_modules = \
|
||||||
settings.dynamic_params_for_other_modules, False
|
settings.dynamic_params_for_other_modules, False
|
||||||
|
|
||||||
search_names = ['append', 'extend', 'insert'] if is_list else ['add', 'update']
|
is_list = sequence.name.string_name == 'list'
|
||||||
comp_arr_parent = get_execution_parent(compare_array)
|
search_names = (['append', 'extend', 'insert'] if is_list else ['add', 'update'])
|
||||||
|
#comp_arr_parent = None
|
||||||
|
|
||||||
added_types = set()
|
added_types = set()
|
||||||
for add_name in search_names:
|
for add_name in search_names:
|
||||||
try:
|
try:
|
||||||
possible_names = module.used_names[add_name]
|
possible_names = module_context.module_node.used_names[add_name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
for name in possible_names:
|
for name in possible_names:
|
||||||
|
context_node = context.get_node()
|
||||||
|
if not (context_node.start_pos < name.start_pos < context_node.end_pos):
|
||||||
|
continue
|
||||||
|
'''
|
||||||
# Check if the original scope is an execution. If it is, one
|
# Check if the original scope is an execution. If it is, one
|
||||||
# can search for the same statement, that is in the module
|
# can search for the same statement, that is in the module
|
||||||
# dict. Executions are somewhat special in jedi, since they
|
# dict. Executions are somewhat special in jedi, since they
|
||||||
@@ -772,6 +768,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
|||||||
# improves Jedi's speed for array lookups, since we
|
# improves Jedi's speed for array lookups, since we
|
||||||
# don't have to check the whole source tree anymore.
|
# don't have to check the whole source tree anymore.
|
||||||
continue
|
continue
|
||||||
|
'''
|
||||||
trailer = name.parent
|
trailer = name.parent
|
||||||
power = trailer.parent
|
power = trailer.parent
|
||||||
trailer_pos = power.children.index(trailer)
|
trailer_pos = power.children.index(trailer)
|
||||||
@@ -784,36 +781,42 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
|||||||
or execution_trailer.children[0] != '(' \
|
or execution_trailer.children[0] != '(' \
|
||||||
or execution_trailer.children[1] == ')':
|
or execution_trailer.children[1] == ')':
|
||||||
continue
|
continue
|
||||||
power = helpers.call_of_leaf(name, cut_own_trailer=True)
|
|
||||||
# InstanceElements are special, because they don't get copied,
|
|
||||||
# but have this wrapper around them.
|
|
||||||
if isinstance(comp_arr_parent, er.InstanceElement):
|
|
||||||
power = er.get_instance_el(evaluator, comp_arr_parent.instance, power)
|
|
||||||
|
|
||||||
if evaluator.recursion_detector.push_stmt(power):
|
random_context = context.create_context(name)
|
||||||
|
if context.evaluator.recursion_detector.push_stmt(power):
|
||||||
# Check for recursion. Possible by using 'extend' in
|
# Check for recursion. Possible by using 'extend' in
|
||||||
# combination with function calls.
|
# combination with function calls.
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
if compare_array in evaluator.eval_element(power):
|
found = helpers.evaluate_call_of_leaf(
|
||||||
|
random_context,
|
||||||
|
name,
|
||||||
|
cut_own_trailer=True
|
||||||
|
)
|
||||||
|
if sequence in found:
|
||||||
# The arrays match. Now add the results
|
# The arrays match. Now add the results
|
||||||
added_types |= check_additions(execution_trailer.children[1], add_name)
|
added_types |= find_additions(
|
||||||
|
random_context,
|
||||||
|
execution_trailer.children[1],
|
||||||
|
add_name
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
evaluator.recursion_detector.pop_stmt()
|
context.evaluator.recursion_detector.pop_stmt()
|
||||||
|
|
||||||
# reset settings
|
# reset settings
|
||||||
settings.dynamic_params_for_other_modules = temp_param_add
|
settings.dynamic_params_for_other_modules = temp_param_add
|
||||||
debug.dbg('Dynamic array result %s' % added_types, color='MAGENTA')
|
debug.dbg('Dynamic array result %s' % added_types, color='MAGENTA')
|
||||||
return added_types
|
return added_types
|
||||||
|
|
||||||
|
|
||||||
def check_array_instances(evaluator, instance):
|
def get_dynamic_array_instance(instance):
|
||||||
"""Used for set() and list() instances."""
|
"""Used for set() and list() instances."""
|
||||||
if not settings.dynamic_array_additions:
|
if not settings.dynamic_array_additions:
|
||||||
return instance.var_args
|
return instance.var_args
|
||||||
|
|
||||||
ai = _ArrayInstance(evaluator, instance)
|
ai = _ArrayInstance(instance)
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
return param.Arguments(evaluator, instance, [AlreadyEvaluated([ai])])
|
return param.ValuesArguments([[ai]])
|
||||||
|
|
||||||
|
|
||||||
class _ArrayInstance(object):
|
class _ArrayInstance(object):
|
||||||
@@ -827,29 +830,25 @@ class _ArrayInstance(object):
|
|||||||
and therefore doesn't need `names_dicts`, `py__bool__` and so on, because
|
and therefore doesn't need `names_dicts`, `py__bool__` and so on, because
|
||||||
we don't use these operations in `builtins.py`.
|
we don't use these operations in `builtins.py`.
|
||||||
"""
|
"""
|
||||||
def __init__(self, evaluator, instance):
|
def __init__(self, instance):
|
||||||
self.evaluator = evaluator
|
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.var_args = instance.var_args
|
self.var_args = instance.var_args
|
||||||
|
|
||||||
def py__iter__(self):
|
def py__iter__(self):
|
||||||
raise NotImplementedError
|
var_args = self.var_args
|
||||||
try:
|
try:
|
||||||
_, first_nodes = next(self.var_args.unpack())
|
_, lazy_context = next(var_args.unpack())
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
types = set()
|
pass
|
||||||
else:
|
else:
|
||||||
types = unite(self.evaluator.eval_element(node) for node in first_nodes)
|
for lazy in py__iter__(self.instance.evaluator, lazy_context.infer()):
|
||||||
for types in py__iter__(self.evaluator, types, first_nodes[0]):
|
yield lazy
|
||||||
yield types
|
|
||||||
|
|
||||||
module = self.var_args.get_parent_until()
|
from jedi.evaluate import param
|
||||||
if module is None:
|
if isinstance(var_args, param.TreeArguments):
|
||||||
return
|
additions = _check_array_additions(var_args.context, self.instance)
|
||||||
is_list = str(self.instance.name) == 'list'
|
for addition in additions:
|
||||||
additions = _check_array_additions(self.evaluator, self.instance, module, is_list)
|
yield addition
|
||||||
if additions:
|
|
||||||
yield additions
|
|
||||||
|
|
||||||
|
|
||||||
class Slice(context.Context):
|
class Slice(context.Context):
|
||||||
|
|||||||
Reference in New Issue
Block a user