forked from VimPlug/jedi
Start implemeting contexts with specialized TreeNameDefinitions to avoid gambling with the parser.
This commit is contained in:
@@ -29,6 +29,7 @@ def get_call_signature_param_names(call_signatures):
|
||||
def filter_names(evaluator, completion_names, stack, like_name):
|
||||
comp_dct = {}
|
||||
for name in set(completion_names):
|
||||
print(name)
|
||||
if settings.case_insensitive_completion \
|
||||
and str(name).lower().startswith(like_name.lower()) \
|
||||
or str(name).startswith(like_name):
|
||||
@@ -193,11 +194,11 @@ class Completion:
|
||||
|
||||
def _trailer_completions(self, atom_expr):
|
||||
user_scope = get_user_scope(self._module, self._position)
|
||||
scopes = self._evaluator.eval_element(atom_expr)
|
||||
contexts = self._evaluator.eval_element(self._evaluator.create_context(atom_expr), atom_expr)
|
||||
completion_names = []
|
||||
debug.dbg('trailer completion scopes: %s', scopes)
|
||||
for s in scopes:
|
||||
for filter in s.get_filters(search_global=False, origin_scope=user_scope):
|
||||
debug.dbg('trailer completion contexts: %s', contexts)
|
||||
for context in contexts:
|
||||
for filter in context.get_filters(search_global=False, origin_scope=user_scope):
|
||||
completion_names += filter.values()
|
||||
return completion_names
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import copy
|
||||
|
||||
from jedi.cache import underscore_memoization
|
||||
from jedi.evaluate import helpers
|
||||
from jedi.evaluate.representation import ModuleWrapper
|
||||
from jedi.evaluate.representation import ModuleContext
|
||||
from jedi.evaluate.compiled import mixed
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class MixedModule(object):
|
||||
self._namespaces = namespaces
|
||||
|
||||
self._namespace_objects = [type('jedi_namespace', (), n) for n in namespaces]
|
||||
self._wrapped_module = ModuleWrapper(evaluator, parser_module)
|
||||
self._wrapped_module = ModuleContext(evaluator, parser_module)
|
||||
# Usually we are dealing with very small code sizes when it comes to
|
||||
# interpreter modules. In this case we just copy the whole syntax tree
|
||||
# to be able to modify it.
|
||||
|
||||
@@ -109,20 +109,20 @@ class Evaluator(object):
|
||||
self.recursion_detector = recursion.RecursionDetector(self)
|
||||
self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self)
|
||||
|
||||
def wrap(self, element):
|
||||
def wrap(self, element, parent_context=None):
|
||||
if isinstance(element, (er.Wrapper, er.InstanceElement,
|
||||
er.ModuleWrapper, er.FunctionExecution, er.Instance, compiled.CompiledObject)) or element is None:
|
||||
er.ModuleContext, er.FunctionExecution, er.Instance, compiled.CompiledObject)) or element is None:
|
||||
# TODO this is so ugly, please refactor.
|
||||
return element
|
||||
|
||||
if element.type == 'classdef':
|
||||
return er.Class(self, element)
|
||||
return er.ClassContext(self, element, parent_context)
|
||||
elif element.type == 'funcdef':
|
||||
return er.Function(self, element)
|
||||
elif element.type == 'lambda':
|
||||
return er.LambdaWrapper(self, element)
|
||||
elif element.type == 'file_input':
|
||||
return er.ModuleWrapper(self, element)
|
||||
return er.ModuleContext(self, element)
|
||||
else:
|
||||
return element
|
||||
|
||||
@@ -145,7 +145,7 @@ class Evaluator(object):
|
||||
#@memoize_default(default=[], evaluator_is_first_arg=True)
|
||||
#@recursion.recursion_decorator
|
||||
@debug.increase_indent
|
||||
def eval_statement(self, stmt, seek_name=None):
|
||||
def eval_statement(self, context, stmt, seek_name=None):
|
||||
"""
|
||||
The starting point of the completion. A statement always owns a call
|
||||
list, which are the calls, that a statement does. In case multiple
|
||||
@@ -156,7 +156,7 @@ class Evaluator(object):
|
||||
"""
|
||||
debug.dbg('eval_statement %s (%s)', stmt, seek_name)
|
||||
rhs = stmt.get_rhs()
|
||||
types = self.eval_element(rhs)
|
||||
types = self.eval_element(context, rhs)
|
||||
|
||||
if seek_name:
|
||||
types = finder.check_tuple_assignments(self, types, seek_name)
|
||||
@@ -177,13 +177,13 @@ class Evaluator(object):
|
||||
# only in for loops without clutter, because they are
|
||||
# predictable. Also only do it, if the variable is not a tuple.
|
||||
node = for_stmt.get_input_node()
|
||||
for_iterables = self.eval_element(node)
|
||||
for_iterables = self.eval_element(context, node)
|
||||
ordered = list(iterable.py__iter__(self, for_iterables, node))
|
||||
|
||||
for index_types in ordered:
|
||||
dct = {str(for_stmt.children[1]): index_types}
|
||||
self.predefined_if_name_dict_dict[for_stmt] = dct
|
||||
t = self.eval_element(rhs)
|
||||
t = self.eval_element(context, rhs)
|
||||
left = precedence.calculate(self, left, operator, t)
|
||||
types = left
|
||||
if ordered:
|
||||
@@ -196,11 +196,11 @@ class Evaluator(object):
|
||||
debug.dbg('eval_statement result %s', types)
|
||||
return types
|
||||
|
||||
def eval_element(self, element):
|
||||
def eval_element(self, context, element):
|
||||
if isinstance(element, iterable.AlreadyEvaluated):
|
||||
return set(element)
|
||||
elif isinstance(element, iterable.MergedNodes):
|
||||
return iterable.unite(self.eval_element(e) for e in element)
|
||||
return iterable.unite(self.eval_element(context, e) for e in element)
|
||||
|
||||
if_stmt = element.get_parent_until((tree.IfStmt, tree.ForStmt, tree.IsScope))
|
||||
predefined_if_name_dict = self.predefined_if_name_dict_dict.get(if_stmt)
|
||||
@@ -249,21 +249,21 @@ class Evaluator(object):
|
||||
for name_dict in name_dicts:
|
||||
self.predefined_if_name_dict_dict[if_stmt] = name_dict
|
||||
try:
|
||||
result |= self._eval_element_not_cached(element)
|
||||
result |= self._eval_element_not_cached(context, element)
|
||||
finally:
|
||||
del self.predefined_if_name_dict_dict[if_stmt]
|
||||
return result
|
||||
else:
|
||||
return self._eval_element_if_evaluated(element)
|
||||
return self._eval_element_cached(element)
|
||||
return self._eval_element_if_evaluated(context, element)
|
||||
return self._eval_element_cached(context, element)
|
||||
else:
|
||||
if predefined_if_name_dict:
|
||||
return self._eval_element_not_cached(element)
|
||||
return self._eval_element_not_cached(context, element)
|
||||
else:
|
||||
return self._eval_element_if_evaluated(element)
|
||||
return self._eval_element_cached(element)
|
||||
return self._eval_element_if_evaluated(context, element)
|
||||
return self._eval_element_cached(context, element)
|
||||
|
||||
def _eval_element_if_evaluated(self, element):
|
||||
def _eval_element_if_evaluated(self, context, element):
|
||||
"""
|
||||
TODO This function is temporary: Merge with eval_element.
|
||||
"""
|
||||
@@ -272,19 +272,19 @@ class Evaluator(object):
|
||||
parent = parent.parent
|
||||
predefined_if_name_dict = self.predefined_if_name_dict_dict.get(parent)
|
||||
if predefined_if_name_dict is not None:
|
||||
return self._eval_element_not_cached(element)
|
||||
return self._eval_element_cached(element)
|
||||
return self._eval_element_not_cached(context, element)
|
||||
return self._eval_element_cached(context, element)
|
||||
|
||||
@memoize_default(default=set(), evaluator_is_first_arg=True)
|
||||
def _eval_element_cached(self, element):
|
||||
return self._eval_element_not_cached(element)
|
||||
def _eval_element_cached(self, context, element):
|
||||
return self._eval_element_not_cached(context, element)
|
||||
|
||||
@debug.increase_indent
|
||||
def _eval_element_not_cached(self, element):
|
||||
def _eval_element_not_cached(self, context, element):
|
||||
debug.dbg('eval_element %s@%s', element, element.start_pos)
|
||||
types = set()
|
||||
if isinstance(element, (tree.Name, tree.Literal)) or tree.is_node(element, 'atom'):
|
||||
types = self._eval_atom(element)
|
||||
types = self._eval_atom(context, element)
|
||||
elif isinstance(element, tree.Keyword):
|
||||
# For False/True/None
|
||||
if element.value in ('False', 'True', 'None'):
|
||||
@@ -295,12 +295,12 @@ class Evaluator(object):
|
||||
elif element.isinstance(er.LambdaWrapper):
|
||||
types = set([element]) # TODO this is no real evaluation.
|
||||
elif element.type == 'expr_stmt':
|
||||
types = self.eval_statement(element)
|
||||
types = self.eval_statement(context, element)
|
||||
elif element.type in ('power', 'atom_expr'):
|
||||
types = self._eval_atom(element.children[0])
|
||||
types = self._eval_atom(context, element.children[0])
|
||||
for trailer in element.children[1:]:
|
||||
if trailer == '**': # has a power operation.
|
||||
right = self.eval_element(element.children[2])
|
||||
right = self.eval_element(context, element.children[2])
|
||||
types = set(precedence.calculate(self, types, trailer, right))
|
||||
break
|
||||
types = self.eval_trailer(types, trailer)
|
||||
@@ -308,31 +308,31 @@ class Evaluator(object):
|
||||
# The implicit tuple in statements.
|
||||
types = set([iterable.ImplicitTuple(self, element)])
|
||||
elif element.type in ('not_test', 'factor'):
|
||||
types = self.eval_element(element.children[-1])
|
||||
types = self.eval_element(context, element.children[-1])
|
||||
for operator in element.children[:-1]:
|
||||
types = set(precedence.factor_calculate(self, types, operator))
|
||||
elif element.type == 'test':
|
||||
# `x if foo else y` case.
|
||||
types = (self.eval_element(element.children[0]) |
|
||||
self.eval_element(element.children[-1]))
|
||||
types = (self.eval_element(context, element.children[0]) |
|
||||
self.eval_element(context, element.children[-1]))
|
||||
elif element.type == 'operator':
|
||||
# Must be an ellipsis, other operators are not evaluated.
|
||||
assert element.value == '...'
|
||||
types = set([compiled.create(self, Ellipsis)])
|
||||
elif element.type == 'dotted_name':
|
||||
types = self._eval_atom(element.children[0])
|
||||
types = self._eval_atom(context, element.children[0])
|
||||
for next_name in element.children[2::2]:
|
||||
types = set(chain.from_iterable(self.find_types(typ, next_name)
|
||||
for typ in types))
|
||||
types = types
|
||||
elif element.type == 'eval_input':
|
||||
types = self._eval_element_not_cached(element.children[0])
|
||||
types = self._eval_element_not_cached(context, element.children[0])
|
||||
else:
|
||||
types = precedence.calculate_children(self, element.children)
|
||||
debug.dbg('eval_element result %s', types)
|
||||
return types
|
||||
|
||||
def _eval_atom(self, atom):
|
||||
def _eval_atom(self, context, atom):
|
||||
"""
|
||||
Basically to process ``atom`` nodes. The parser sometimes doesn't
|
||||
generate the node (because it has just one child). In that case an atom
|
||||
@@ -442,25 +442,25 @@ class Evaluator(object):
|
||||
debug.dbg('execute result: %s in %s', types, obj)
|
||||
return types
|
||||
|
||||
def goto_definitions(self, name):
|
||||
def goto_definitions(self, context, name):
|
||||
def_ = name.get_definition()
|
||||
is_simple_name = name.parent.type not in ('power', 'trailer')
|
||||
if is_simple_name:
|
||||
if name.parent.type in ('file_input', 'classdef', 'funcdef'):
|
||||
return [self.wrap(name.parent)]
|
||||
if def_.type == 'expr_stmt' and name in def_.get_defined_names():
|
||||
return self.eval_statement(def_, name)
|
||||
return self.eval_statement(context, def_, name)
|
||||
elif def_.type == 'for_stmt':
|
||||
container_types = self.eval_element(def_.children[3])
|
||||
container_types = self.eval_element(context, def_.children[3])
|
||||
for_types = iterable.py__iter__types(self, container_types, def_.children[3])
|
||||
return finder.check_tuple_assignments(self, for_types, name)
|
||||
elif def_.type in ('import_from', 'import_name'):
|
||||
return imports.ImportWrapper(self, name).follow()
|
||||
|
||||
call = helpers.call_of_leaf(name)
|
||||
return self.eval_element(call)
|
||||
return self.eval_element(context, call)
|
||||
|
||||
def goto(self, name):
|
||||
def goto(self, context, name):
|
||||
def resolve_implicit_imports(names):
|
||||
for name in names:
|
||||
if isinstance(name.parent, helpers.FakeImport):
|
||||
@@ -480,13 +480,13 @@ class Evaluator(object):
|
||||
trailer = trailer.parent
|
||||
if trailer.type != 'classdef':
|
||||
if trailer.type == 'decorator':
|
||||
types = self.eval_element(trailer.children[1])
|
||||
types = self.eval_element(context, trailer.children[1])
|
||||
else:
|
||||
i = trailer.parent.children.index(trailer)
|
||||
to_evaluate = trailer.parent.children[:i]
|
||||
types = self.eval_element(to_evaluate[0])
|
||||
types = self.eval_element(context, to_evaluate[0])
|
||||
for trailer in to_evaluate[1:]:
|
||||
types = self.eval_trailer(types, trailer)
|
||||
types = self.eval_trailer(context, types, trailer)
|
||||
param_names = []
|
||||
for typ in types:
|
||||
try:
|
||||
@@ -511,7 +511,7 @@ class Evaluator(object):
|
||||
if index > 0:
|
||||
new_dotted = helpers.deep_ast_copy(par)
|
||||
new_dotted.children[index - 1:] = []
|
||||
types = self.eval_element(new_dotted)
|
||||
types = self.eval_element(context, new_dotted)
|
||||
return resolve_implicit_imports(iterable.unite(
|
||||
self.find_types(typ, name, is_goto=True) for typ in types
|
||||
))
|
||||
@@ -519,7 +519,7 @@ class Evaluator(object):
|
||||
scope = name.get_parent_scope()
|
||||
if tree.is_node(par, 'trailer') and par.children[0] == '.':
|
||||
call = helpers.call_of_leaf(name, cut_own_trailer=True)
|
||||
types = self.eval_element(call)
|
||||
types = self.eval_element(context, call)
|
||||
return resolve_implicit_imports(iterable.unite(
|
||||
self.find_types(typ, name, is_goto=True) for typ in types
|
||||
))
|
||||
@@ -530,3 +530,9 @@ class Evaluator(object):
|
||||
stmt = name
|
||||
return self.find_types(scope, name, stmt.start_pos,
|
||||
search_global=True, is_goto=True)
|
||||
|
||||
def create_context(self, node):
|
||||
scope = node.get_parent_scope()
|
||||
if scope.get_parent_scope() is not None:
|
||||
raise NotImplementedError
|
||||
return self.wrap(scope)
|
||||
|
||||
@@ -271,15 +271,10 @@ class CompiledName(FakeName):
|
||||
def is_definition(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
@underscore_memoization
|
||||
def parent(self):
|
||||
def infer(self):
|
||||
module = self._compiled_obj.get_parent_until()
|
||||
return _create_from_name(self._evaluator, module, self._compiled_obj, self.name)
|
||||
|
||||
@parent.setter
|
||||
def parent(self, value):
|
||||
pass # Just ignore this, FakeName tries to overwrite the parent attribute.
|
||||
return [_create_from_name(self._evaluator, module, self._compiled_obj, self.name)]
|
||||
|
||||
|
||||
class LazyNamesDict(object):
|
||||
|
||||
@@ -9,6 +9,44 @@ from jedi.evaluate import flow_analysis
|
||||
from jedi.common import to_list
|
||||
|
||||
|
||||
class AbstractNameDefinition(object):
|
||||
start_pos = None
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def string_name(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def infer(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class TreeNameDefinition(AbstractNameDefinition):
|
||||
def __init__(self, parent_context, name):
|
||||
self.parent_context = parent_context
|
||||
self._name = name
|
||||
|
||||
def get_parent_flow_context(self):
|
||||
return self.parent_context
|
||||
|
||||
@property
|
||||
def string_name(self):
|
||||
return self._name.value
|
||||
|
||||
@property
|
||||
def start_pos(self):
|
||||
return self._name.start_pos
|
||||
|
||||
def infer(self):
|
||||
# Refactor this, should probably be here.
|
||||
from jedi.evaluate.finder import _name_to_types
|
||||
return _name_to_types(self.parent_context._evaluator, self.parent_context, self._name, None)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s: %s@%s' % (type(self).__name__, self.string_name, self.start_pos)
|
||||
|
||||
|
||||
class AbstractFilter(object):
|
||||
_until_position = None
|
||||
|
||||
@@ -30,10 +68,11 @@ class AbstractFilter(object):
|
||||
|
||||
|
||||
class AbstractUsedNamesFilter(AbstractFilter):
|
||||
def __init__(self, parser_scope, origin_scope=None):
|
||||
def __init__(self, context, parser_scope, origin_scope=None):
|
||||
super(AbstractUsedNamesFilter, self).__init__(origin_scope)
|
||||
self._parser_scope = parser_scope
|
||||
self._used_names = self._parser_scope.get_root_node().used_names
|
||||
self._context = context
|
||||
|
||||
def get(self, name):
|
||||
try:
|
||||
@@ -41,16 +80,19 @@ class AbstractUsedNamesFilter(AbstractFilter):
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
return list(self._filter(names))
|
||||
return self._convert_to_names(self._filter(names))
|
||||
|
||||
def _convert_to_names(self, names):
|
||||
return [TreeNameDefinition(self._context, name) for name in names]
|
||||
|
||||
def values(self):
|
||||
return [name for name_list in self._used_names.values()
|
||||
for name in self._filter(name_list)]
|
||||
return self._convert_to_names(name for name_list in self._used_names.values()
|
||||
for name in self._filter(name_list))
|
||||
|
||||
|
||||
class ParserTreeFilter(AbstractUsedNamesFilter):
|
||||
def __init__(self, evaluator, parser_scope, until_position=None, origin_scope=None):
|
||||
super(ParserTreeFilter, self).__init__(parser_scope, origin_scope)
|
||||
def __init__(self, evaluator, context, parser_scope, until_position=None, origin_scope=None):
|
||||
super(ParserTreeFilter, self).__init__(context, parser_scope, origin_scope)
|
||||
self._until_position = until_position
|
||||
self._evaluator = evaluator
|
||||
|
||||
@@ -65,8 +107,9 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
|
||||
for name in sorted(names, key=lambda name: name.start_pos, reverse=True):
|
||||
stmt = name.get_definition()
|
||||
name_scope = self._evaluator.wrap(stmt.get_parent_scope())
|
||||
check = flow_analysis.break_check(self._evaluator, name_scope,
|
||||
stmt, self._origin_scope)
|
||||
check = flow_analysis.UNSURE
|
||||
#check = flow_analysis.break_check(self._evaluator, name_scope,
|
||||
# stmt, self._origin_scope)
|
||||
if check is not flow_analysis.UNREACHABLE:
|
||||
yield name
|
||||
|
||||
@@ -75,7 +118,7 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
|
||||
|
||||
|
||||
class FunctionExecutionFilter(ParserTreeFilter):
|
||||
def __init__(self, evaluator, parser_scope, executed_function, param_by_name,
|
||||
def __init__(self, evaluator, context, parser_scope, executed_function, param_by_name,
|
||||
until_position=None, origin_scope=None):
|
||||
super(FunctionExecutionFilter, self).__init__(
|
||||
evaluator,
|
||||
@@ -96,8 +139,8 @@ class FunctionExecutionFilter(ParserTreeFilter):
|
||||
|
||||
|
||||
class GlobalNameFilter(AbstractUsedNamesFilter):
|
||||
def __init__(self, parser_scope, origin_scope=None):
|
||||
super(GlobalNameFilter, self).__init__(parser_scope)
|
||||
def __init__(self, context, parser_scope, origin_scope=None):
|
||||
super(GlobalNameFilter, self).__init__(context, parser_scope)
|
||||
|
||||
@to_list
|
||||
def _filter(self, names):
|
||||
@@ -141,7 +184,7 @@ def get_global_filters(evaluator, context, until_position, origin_scope):
|
||||
until_position = None
|
||||
in_func = True
|
||||
|
||||
node = context.get_parent_scope()
|
||||
node = context.parent_context
|
||||
context = evaluator.wrap(node)
|
||||
|
||||
# Add builtins to the global scope.
|
||||
|
||||
@@ -179,7 +179,7 @@ class NameFinder(object):
|
||||
last_names.append(name)
|
||||
continue
|
||||
|
||||
if isinstance(stmt, er.ModuleWrapper):
|
||||
if isinstance(stmt, er.ModuleContext):
|
||||
# In case of REPL completion, we can infer modules names that
|
||||
# don't really have a definition (because they are really just
|
||||
# namespaces). In this case we can just add it.
|
||||
@@ -258,17 +258,19 @@ class NameFinder(object):
|
||||
wrapper parents. We don't want to see AST classes out in the
|
||||
evaluation, so remove them already here!
|
||||
"""
|
||||
for n in names:
|
||||
definition = n.parent
|
||||
if isinstance(definition, (compiled.CompiledObject,
|
||||
iterable.BuiltinMethod)):
|
||||
# TODO this if should really be removed by changing the type of
|
||||
# those classes.
|
||||
yield n
|
||||
elif definition.type in ('funcdef', 'classdef', 'file_input'):
|
||||
yield self._evaluator.wrap(definition).name
|
||||
else:
|
||||
yield n
|
||||
|
||||
return names
|
||||
#for n in names:
|
||||
# definition = n.parent
|
||||
# if isinstance(definition, (compiled.CompiledObject,
|
||||
# iterable.BuiltinMethod)):
|
||||
# # TODO this if should really be removed by changing the type of
|
||||
# # those classes.
|
||||
# yield n
|
||||
# elif definition.type in ('funcdef', 'classdef', 'file_input'):
|
||||
# yield self._evaluator.wrap(definition).name
|
||||
# else:
|
||||
# yield n
|
||||
|
||||
def _check_getattr(self, inst):
|
||||
"""Checks for both __getattr__ and __getattribute__ methods"""
|
||||
@@ -308,8 +310,8 @@ class NameFinder(object):
|
||||
return n
|
||||
|
||||
for name in names:
|
||||
new_types = _name_to_types(self._evaluator, name, self.scope)
|
||||
if isinstance(self.scope, (er.Class, er.Instance)) and attribute_lookup:
|
||||
new_types = name.infer()
|
||||
if isinstance(self.scope, (er.ClassContext, er.Instance)) and attribute_lookup:
|
||||
types |= set(self._resolve_descriptors(name, new_types))
|
||||
else:
|
||||
types |= set(new_types)
|
||||
@@ -347,7 +349,7 @@ def _get_global_stmt_scopes(evaluator, global_stmt, name):
|
||||
|
||||
|
||||
@memoize_default(set(), evaluator_is_first_arg=True)
|
||||
def _name_to_types(evaluator, name, scope):
|
||||
def _name_to_types(evaluator, context, name, scope):
|
||||
types = []
|
||||
typ = name.get_definition()
|
||||
if typ.isinstance(tree.ForStmt):
|
||||
@@ -365,7 +367,7 @@ def _name_to_types(evaluator, name, scope):
|
||||
elif isinstance(typ, tree.Param):
|
||||
types = _eval_param(evaluator, typ, scope)
|
||||
elif typ.isinstance(tree.ExprStmt):
|
||||
types = _remove_statements(evaluator, typ, name)
|
||||
types = _remove_statements(evaluator, context, typ, name)
|
||||
elif typ.isinstance(tree.WithStmt):
|
||||
types = evaluator.eval_element(typ.node_from_name(name))
|
||||
elif isinstance(typ, tree.Import):
|
||||
@@ -393,7 +395,7 @@ def _name_to_types(evaluator, name, scope):
|
||||
return types
|
||||
|
||||
|
||||
def _remove_statements(evaluator, stmt, name):
|
||||
def _remove_statements(evaluator, context, stmt, name):
|
||||
"""
|
||||
This is the part where statements are being stripped.
|
||||
|
||||
@@ -415,7 +417,7 @@ def _remove_statements(evaluator, stmt, name):
|
||||
pep0484.find_type_from_comment_hint_assign(evaluator, stmt, name)
|
||||
if pep0484types:
|
||||
return pep0484types
|
||||
types |= evaluator.eval_statement(stmt, seek_name=name)
|
||||
types |= evaluator.eval_statement(context, stmt, seek_name=name)
|
||||
|
||||
if check_instance is not None:
|
||||
# class renames
|
||||
|
||||
@@ -33,6 +33,7 @@ UNSURE = Status(None, 'unsure')
|
||||
|
||||
|
||||
def break_check(evaluator, base_scope, stmt, origin_scope=None):
|
||||
raise NotImplementedError
|
||||
element_scope = evaluator.wrap(stmt.get_parent_scope(include_flows=True))
|
||||
# Direct parents get resolved, we filter scopes that are separate branches.
|
||||
# This makes sense for autocompletion and static analysis. For actual
|
||||
|
||||
@@ -30,7 +30,7 @@ def try_iter_content(types, depth=0):
|
||||
|
||||
|
||||
class Arguments(tree.Base):
|
||||
def __init__(self, evaluator, argument_node, trailer=None):
|
||||
def __init__(self, evaluator, context, argument_node, trailer=None):
|
||||
"""
|
||||
The argument_node is either a parser node or a list of evaluated
|
||||
objects. Those evaluated objects may be lists of evaluated objects
|
||||
@@ -39,6 +39,7 @@ class Arguments(tree.Base):
|
||||
:param argument_node: May be an argument_node or a list of nodes.
|
||||
"""
|
||||
self.argument_node = argument_node
|
||||
self._context = context
|
||||
self._evaluator = evaluator
|
||||
self.trailer = trailer # Can be None, e.g. in a class definition.
|
||||
|
||||
@@ -73,7 +74,7 @@ class Arguments(tree.Base):
|
||||
element = self.argument_node[0]
|
||||
from jedi.evaluate.iterable import AlreadyEvaluated
|
||||
if isinstance(element, AlreadyEvaluated):
|
||||
element = list(self._evaluator.eval_element(element))[0]
|
||||
element = list(self._evaluator.eval_element(self._context, element))[0]
|
||||
except IndexError:
|
||||
return None
|
||||
else:
|
||||
@@ -93,14 +94,14 @@ class Arguments(tree.Base):
|
||||
named_args = []
|
||||
for stars, el in self._split():
|
||||
if stars == 1:
|
||||
arrays = self._evaluator.eval_element(el)
|
||||
arrays = self._evaluator.eval_element(self._context, el)
|
||||
iterators = [_iterate_star_args(self._evaluator, a, el, func)
|
||||
for a in arrays]
|
||||
iterators = list(iterators)
|
||||
for values in list(zip_longest(*iterators)):
|
||||
yield None, [v for v in values if v is not None]
|
||||
elif stars == 2:
|
||||
arrays = self._evaluator.eval_element(el)
|
||||
arrays = self._evaluator.eval_element(self._context, el)
|
||||
dicts = [_star_star_dict(self._evaluator, a, el, func)
|
||||
for a in arrays]
|
||||
for dct in dicts:
|
||||
@@ -155,7 +156,7 @@ class Arguments(tree.Base):
|
||||
debug.warning('TypeError: %s expected at least %s arguments, got %s',
|
||||
name, len(arguments), i)
|
||||
raise ValueError
|
||||
values = set(chain.from_iterable(self._evaluator.eval_element(el)
|
||||
values = set(chain.from_iterable(self._evaluator.eval_element(self._context, el)
|
||||
for el in va_values))
|
||||
if not values and not optional:
|
||||
# For the stdlib we always want values. If we don't get them,
|
||||
@@ -172,7 +173,7 @@ class Arguments(tree.Base):
|
||||
def eval_args(self):
|
||||
# TODO this method doesn't work with named args and a lot of other
|
||||
# things. Use unpack.
|
||||
return [self._evaluator.eval_element(el) for stars, el in self._split()]
|
||||
return [self._evaluator.eval_element(self._context, el) for stars, el in self._split()]
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s: %s>' % (type(self).__name__, self.argument_node)
|
||||
@@ -191,7 +192,7 @@ class Arguments(tree.Base):
|
||||
"""
|
||||
for key, element_values in self.unpack():
|
||||
for element in element_values:
|
||||
types = self._evaluator.eval_element(element)
|
||||
types = self._evaluator.eval_element(self._context, element)
|
||||
try_iter_content(types)
|
||||
|
||||
|
||||
|
||||
@@ -59,6 +59,21 @@ from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
|
||||
GlobalNameFilter, DictFilter
|
||||
|
||||
|
||||
class Context(object):
|
||||
def __init__(self, evaluator, parent_context=None):
|
||||
self._evaluator = evaluator
|
||||
self.parent_context = parent_context
|
||||
|
||||
def get_parent_flow_context(self):
|
||||
return self.parent_context
|
||||
|
||||
|
||||
class FlowContext(Context):
|
||||
def get_parent_flow_context(self):
|
||||
if 1:
|
||||
return self.parent_context
|
||||
|
||||
|
||||
class Executed(tree.Base):
|
||||
"""
|
||||
An instance is also an executable - because __init__ is called
|
||||
@@ -152,7 +167,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
|
||||
# This loop adds the names of the self object, copies them and removes
|
||||
# the self.
|
||||
for sub in self.base.subscopes:
|
||||
if isinstance(sub, tree.Class):
|
||||
if isinstance(sub, tree.ClassContext):
|
||||
continue
|
||||
# Get the self name, if there's one.
|
||||
self_name = self._get_func_self_name(sub)
|
||||
@@ -292,18 +307,18 @@ class CompiledInstanceClassFilter(compiled.CompiledObjectFilter):
|
||||
|
||||
|
||||
class InstanceClassFilter(ParserTreeFilter):
|
||||
def __init__(self, evaluator, instance, parser_scope, origin_scope):
|
||||
def __init__(self, evaluator, context, parser_scope, origin_scope):
|
||||
super(InstanceClassFilter, self).__init__(
|
||||
evaluator=evaluator,
|
||||
context=context,
|
||||
parser_scope=parser_scope,
|
||||
origin_scope=origin_scope
|
||||
)
|
||||
self._instance = instance
|
||||
|
||||
def _equals_origin_scope(self):
|
||||
node = self._origin_scope
|
||||
while node is not None:
|
||||
if node == self._parser_scope or node == self._instance:
|
||||
if node == self._parser_scope or node == self._context:
|
||||
return True
|
||||
node = node.get_parent_scope()
|
||||
return False
|
||||
@@ -314,7 +329,7 @@ class InstanceClassFilter(ParserTreeFilter):
|
||||
|
||||
def _filter(self, names):
|
||||
names = super(InstanceClassFilter, self)._filter(names)
|
||||
return [get_instance_el(self._evaluator, self._instance, name, True)
|
||||
return [get_instance_el(self._evaluator, self._context, name, True)
|
||||
for name in names if self._access_possible(name)]
|
||||
|
||||
def _check_flows(self, names):
|
||||
@@ -337,12 +352,12 @@ class SelfNameFilter(InstanceClassFilter):
|
||||
and len(trailer.children) == 2 \
|
||||
and trailer.children[0] == '.':
|
||||
if name.is_definition() and self._access_possible(name):
|
||||
init_execution = self._instance._get_init_execution()
|
||||
init_execution = self._context._get_init_execution()
|
||||
# Hopefully we can somehow change this.
|
||||
if init_execution is not None and \
|
||||
init_execution.start_pos < name.start_pos < init_execution.end_pos:
|
||||
name = init_execution.name_for_position(name.start_pos)
|
||||
yield get_instance_el(self._evaluator, self._instance, name)
|
||||
yield get_instance_el(self._evaluator, self._context, name)
|
||||
|
||||
|
||||
class LazyInstanceDict(object):
|
||||
@@ -406,8 +421,8 @@ class InstanceElement(use_metaclass(CachedMetaClass, tree.Base)):
|
||||
@memoize_default()
|
||||
def parent(self):
|
||||
par = self.var.parent
|
||||
if isinstance(par, Class) and par == self.instance.base \
|
||||
or not isinstance(self.instance.base, (tree.Class, Class)) \
|
||||
if isinstance(par, ClassContext) and par == self.instance.base \
|
||||
or not isinstance(self.instance.base, (tree.Class, ClassContext)) \
|
||||
or isinstance(par, tree.Class) \
|
||||
and par == self.instance.base.base:
|
||||
par = self.instance
|
||||
@@ -501,13 +516,13 @@ class Wrapper(tree.Base):
|
||||
return helpers.FakeName(unicode(name), self, name.start_pos)
|
||||
|
||||
|
||||
class Class(use_metaclass(CachedMetaClass, Wrapper)):
|
||||
class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)):
|
||||
"""
|
||||
This class is not only important to extend `tree.Class`, it is also a
|
||||
important for descriptors (if the descriptor methods are evaluated or not).
|
||||
"""
|
||||
def __init__(self, evaluator, base):
|
||||
self._evaluator = evaluator
|
||||
def __init__(self, evaluator, base, parent_context):
|
||||
super(ClassContext, self).__init__(evaluator, parent_context=parent_context)
|
||||
self.base = base
|
||||
|
||||
@memoize_default(default=())
|
||||
@@ -547,7 +562,7 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)):
|
||||
def py__bases__(self):
|
||||
arglist = self.base.get_super_arglist()
|
||||
if arglist:
|
||||
args = param.Arguments(self._evaluator, arglist)
|
||||
args = param.Arguments(self._evaluator, self, arglist)
|
||||
return list(chain.from_iterable(args.eval_args()))
|
||||
else:
|
||||
return [compiled.create(self._evaluator, object)]
|
||||
@@ -577,14 +592,14 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)):
|
||||
|
||||
def get_filters(self, search_global, until_position=None, origin_scope=None, is_instance=False):
|
||||
if search_global:
|
||||
yield ParserTreeFilter(self._evaluator, self.base, until_position, origin_scope=origin_scope)
|
||||
yield ParserTreeFilter(self._evaluator, self, self.base, until_position, origin_scope=origin_scope)
|
||||
else:
|
||||
for scope in self.py__mro__():
|
||||
if isinstance(scope, compiled.CompiledObject):
|
||||
for filter in scope.get_filters(is_instance=is_instance):
|
||||
yield filter
|
||||
else:
|
||||
yield ParserTreeFilter(self._evaluator, scope.base, origin_scope=origin_scope)
|
||||
yield ParserTreeFilter(self._evaluator, self, scope.base, origin_scope=origin_scope)
|
||||
|
||||
def is_class(self):
|
||||
return True
|
||||
@@ -684,7 +699,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)):
|
||||
|
||||
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
||||
if search_global:
|
||||
yield ParserTreeFilter(self._evaluator, self.base, until_position, origin_scope=origin_scope)
|
||||
yield ParserTreeFilter(self._evaluator, self, self.base, until_position, origin_scope=origin_scope)
|
||||
else:
|
||||
scope = self.py__class__()
|
||||
for filter in scope.get_filters(search_global=False, origin_scope=origin_scope):
|
||||
@@ -862,7 +877,7 @@ class FunctionExecution(Executed):
|
||||
del evaluator.predefined_if_name_dict_dict[for_stmt]
|
||||
|
||||
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
||||
yield FunctionExecutionFilter(self._evaluator, self._original_function,
|
||||
yield FunctionExecutionFilter(self._evaluator, self, self._original_function,
|
||||
self._copied_funcdef,
|
||||
self.param_by_name,
|
||||
until_position,
|
||||
@@ -923,7 +938,9 @@ class GlobalName(helpers.FakeName):
|
||||
name.start_pos, is_definition=True)
|
||||
|
||||
|
||||
class ModuleWrapper(use_metaclass(CachedMetaClass, tree.Module, Wrapper)):
|
||||
class ModuleContext(use_metaclass(CachedMetaClass, tree.Module, Wrapper)):
|
||||
parent_context = None
|
||||
|
||||
def __init__(self, evaluator, module, parent_module=None):
|
||||
self._evaluator = evaluator
|
||||
self.base = self._module = module
|
||||
@@ -942,11 +959,12 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, tree.Module, Wrapper)):
|
||||
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
||||
yield ParserTreeFilter(
|
||||
self._evaluator,
|
||||
self,
|
||||
self._module,
|
||||
until_position,
|
||||
origin_scope=origin_scope
|
||||
)
|
||||
yield GlobalNameFilter(self._module)
|
||||
yield GlobalNameFilter(self, self._module)
|
||||
yield DictFilter(self._sub_modules_dict())
|
||||
yield DictFilter(self._module_attributes_dict())
|
||||
# TODO
|
||||
|
||||
Reference in New Issue
Block a user