mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
Cleanup the finder.
This commit is contained in:
@@ -21,9 +21,7 @@ from jedi.common import unite
|
||||
from jedi import settings
|
||||
from jedi.evaluate import representation as er
|
||||
from jedi.evaluate.instance import AbstractInstanceContext
|
||||
from jedi.evaluate import dynamic
|
||||
from jedi.evaluate import compiled
|
||||
from jedi.evaluate import docstrings
|
||||
from jedi.evaluate import pep0484
|
||||
from jedi.evaluate import iterable
|
||||
from jedi.evaluate import imports
|
||||
@@ -31,64 +29,7 @@ from jedi.evaluate import analysis
|
||||
from jedi.evaluate import flow_analysis
|
||||
from jedi.evaluate import param
|
||||
from jedi.evaluate import helpers
|
||||
from jedi.evaluate.filters import get_global_filters, TreeNameDefinition
|
||||
|
||||
|
||||
def filter_after_position(names, position, origin=None):
|
||||
"""
|
||||
Removes all names after a certain position. If position is None, just
|
||||
returns the names list.
|
||||
"""
|
||||
if position is None:
|
||||
return names
|
||||
|
||||
names_new = []
|
||||
for n in names:
|
||||
# Filter positions and also allow list comprehensions and lambdas.
|
||||
if n.start_pos[0] is not None and n.start_pos < position:
|
||||
names_new.append(n)
|
||||
elif isinstance(n.get_definition(), (tree.CompFor, tree.Lambda)):
|
||||
if origin is not None and origin.get_definition() != n.get_definition():
|
||||
# This is extremely hacky. A transition that we have to use
|
||||
# until we get rid of names_dicts.
|
||||
continue
|
||||
names_new.append(n)
|
||||
return names_new
|
||||
|
||||
|
||||
def is_comprehension_name(name, origin):
|
||||
definition = name.get_definition()
|
||||
# TODO This is really hacky. It just compares the two definitions. This
|
||||
# fails tests and is in general just a temporary way.
|
||||
return definition.type == 'comp_for' and origin.get_definition().type != definition.type
|
||||
|
||||
|
||||
def filter_definition_names(names, origin, position=None):
|
||||
"""
|
||||
Filter names that are actual definitions in a scope. Names that are just
|
||||
used will be ignored.
|
||||
"""
|
||||
if not names:
|
||||
return []
|
||||
|
||||
# Just calculate the scope from the first
|
||||
stmt = names[0].get_definition()
|
||||
scope = stmt.get_parent_scope()
|
||||
|
||||
if not (isinstance(scope, er.FunctionExecution) and
|
||||
isinstance(scope.base, LambdaWrapper)):
|
||||
names = filter_after_position(names, position, origin)
|
||||
names = [name for name in names
|
||||
if name.is_definition() and not is_comprehension_name(name, origin)]
|
||||
|
||||
# Private name mangling (compile.c) disallows access on names
|
||||
# preceeded by two underscores `__` if used outside of the class. Names
|
||||
# that also end with two underscores (e.g. __id__) are not affected.
|
||||
for name in list(names):
|
||||
if name.value.startswith('__') and not name.value.endswith('__'):
|
||||
if filter_private_variable(scope, origin):
|
||||
names.remove(name)
|
||||
return names
|
||||
from jedi.evaluate.filters import get_global_filters
|
||||
|
||||
|
||||
class NameFinder(object):
|
||||
@@ -111,8 +52,6 @@ class NameFinder(object):
|
||||
:params bool attribute_lookup: Tell to logic if we're accessing the
|
||||
attribute or the contents of e.g. a function.
|
||||
"""
|
||||
# TODO rename scopes to names_dicts
|
||||
|
||||
names = self.filter_name(filters)
|
||||
if self._found_predefined_types is not None and names:
|
||||
check = flow_analysis.reachability_check(
|
||||
@@ -149,100 +88,6 @@ class NameFinder(object):
|
||||
else:
|
||||
return self._context.get_filters(search_global, self._position, origin_scope=origin_scope)
|
||||
|
||||
def names_dict_lookup(self, names_dict, position):
|
||||
def get_param(scope, el):
|
||||
if isinstance(el.get_parent_until(tree.Param), tree.Param):
|
||||
return scope.param_by_name(str(el))
|
||||
return el
|
||||
|
||||
try:
|
||||
names = names_dict[self._string_name]
|
||||
if not names: # We want names, otherwise stop.
|
||||
return []
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
names = filter_definition_names(names, self._name, position)
|
||||
|
||||
name_scope = None
|
||||
# Only the names defined in the last position are valid definitions.
|
||||
last_names = []
|
||||
for name in reversed(sorted(names, key=lambda name: name.start_pos)):
|
||||
stmt = name.get_definition()
|
||||
name_scope = self._evaluator.wrap(stmt.get_parent_scope())
|
||||
|
||||
if isinstance(self._context, er.Instance) and not isinstance(name_scope, er.Instance):
|
||||
# Instances should not be checked for positioning, because we
|
||||
# don't know in which order the functions are called.
|
||||
last_names.append(name)
|
||||
continue
|
||||
|
||||
if isinstance(name_scope, compiled.CompiledObject):
|
||||
# Let's test this. TODO need comment. shouldn't this be
|
||||
# filtered before?
|
||||
last_names.append(name)
|
||||
continue
|
||||
|
||||
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.
|
||||
last_names.append(name)
|
||||
continue
|
||||
|
||||
if isinstance(name, compiled.CompiledName) \
|
||||
or isinstance(name, er.InstanceName) and isinstance(name._origin_name, compiled.CompiledName):
|
||||
last_names.append(name)
|
||||
continue
|
||||
|
||||
if isinstance(self._name, tree.Name):
|
||||
origin_scope = self._name.get_parent_until(tree.Scope, reverse=True)
|
||||
scope = self._name
|
||||
check = None
|
||||
while True:
|
||||
scope = scope.parent
|
||||
if scope.type in ("if_stmt", "for_stmt"):
|
||||
# TODO try removing for_stmt.
|
||||
try:
|
||||
name_dict = self.context.predefined_names[scope]
|
||||
types = set(name_dict[self._string_name])
|
||||
except KeyError:
|
||||
continue
|
||||
else:
|
||||
if self._name.start_pos < scope.children[1].end_pos:
|
||||
# It doesn't make any sense to check if
|
||||
# statements in the if statement itself, just
|
||||
# deliver types.
|
||||
self._found_predefined_types = types
|
||||
else:
|
||||
check = flow_analysis.reachability_check(
|
||||
self._context, self._context, origin_scope)
|
||||
if check is flow_analysis.UNREACHABLE:
|
||||
self._found_predefined_types = set()
|
||||
else:
|
||||
self._found_predefined_types = types
|
||||
break
|
||||
if isinstance(scope, tree.IsScope) or scope is None:
|
||||
break
|
||||
else:
|
||||
origin_scope = None
|
||||
|
||||
if isinstance(stmt.parent, compiled.CompiledObject):
|
||||
# TODO seriously? this is stupid.
|
||||
continue
|
||||
check = flow_analysis.reachability_check(self._context, name_scope,
|
||||
stmt, origin_scope)
|
||||
if check is not flow_analysis.UNREACHABLE:
|
||||
last_names.append(name)
|
||||
|
||||
if check is flow_analysis.REACHABLE:
|
||||
break
|
||||
|
||||
if isinstance(name_scope, er.FunctionExecution):
|
||||
# Replace params
|
||||
return [get_param(name_scope, n) for n in last_names]
|
||||
return last_names
|
||||
|
||||
def filter_name(self, filters):
|
||||
"""
|
||||
Searches names that are defined in a scope (the different
|
||||
@@ -270,27 +115,7 @@ class NameFinder(object):
|
||||
break
|
||||
debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self._string_name,
|
||||
self._context, names, self._position)
|
||||
return list(self._clean_names(names))
|
||||
|
||||
def _clean_names(self, names):
|
||||
"""
|
||||
``NameFinder.filter_name`` should only output names with correct
|
||||
wrapper parents. We don't want to see AST classes out in the
|
||||
evaluation, so remove them already here!
|
||||
"""
|
||||
|
||||
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
|
||||
return list(names)
|
||||
|
||||
def _check_getattr(self, inst):
|
||||
"""Checks for both __getattr__ and __getattribute__ methods"""
|
||||
@@ -354,9 +179,6 @@ def _name_to_types(evaluator, context, name):
|
||||
container_types = context.eval_node(node.children[3])
|
||||
for_types = iterable.py__iter__types(evaluator, container_types, node.children[3])
|
||||
types = check_tuple_assignments(evaluator, for_types, name)
|
||||
elif isinstance(node, tree.Param):
|
||||
return set() # TODO remove
|
||||
types = _eval_param(evaluator, context, node)
|
||||
elif node.isinstance(tree.ExprStmt):
|
||||
types = _remove_statements(evaluator, context, node, name)
|
||||
elif node.isinstance(tree.WithStmt):
|
||||
@@ -383,8 +205,7 @@ def _name_to_types(evaluator, context, name):
|
||||
for t in exceptions
|
||||
)
|
||||
else:
|
||||
raise DeprecationWarning
|
||||
types = set([node])
|
||||
raise ValueError("Should not happen.")
|
||||
return types
|
||||
|
||||
|
||||
@@ -438,11 +259,6 @@ def _remove_statements(evaluator, context, stmt, name):
|
||||
evaluated.
|
||||
"""
|
||||
types = set()
|
||||
# Remove the statement docstr stuff for now, that has to be
|
||||
# implemented with the evaluator class.
|
||||
#if stmt.docstr:
|
||||
#res_new.append(stmt)
|
||||
|
||||
check_instance = None
|
||||
|
||||
pep0484types = \
|
||||
@@ -459,53 +275,6 @@ def _remove_statements(evaluator, context, stmt, name):
|
||||
return types
|
||||
|
||||
|
||||
def _eval_param(evaluator, context, param, scope):
|
||||
res_new = set()
|
||||
func = param.get_parent_scope()
|
||||
|
||||
cls = func.parent.get_parent_until((tree.Class, tree.Function))
|
||||
|
||||
from jedi.evaluate.param import ExecutedParam, Arguments
|
||||
if isinstance(cls, tree.Class) and param.position_nr == 0 \
|
||||
and not isinstance(param, ExecutedParam):
|
||||
# This is where we add self - if it has never been
|
||||
# instantiated.
|
||||
if isinstance(scope, er.InstanceElement):
|
||||
res_new.add(scope.instance)
|
||||
else:
|
||||
inst = er.Instance(evaluator, context.parent_context.parent_context, context.parent_context,
|
||||
Arguments(evaluator, context),
|
||||
is_generated=True)
|
||||
res_new.add(inst)
|
||||
return res_new
|
||||
|
||||
# Instances are typically faked, if the instance is not called from
|
||||
# outside. Here we check it for __init__ functions and return.
|
||||
if isinstance(func, er.InstanceElement) \
|
||||
and func.instance.is_generated and str(func.name) == '__init__':
|
||||
param = func.var.params[param.position_nr]
|
||||
|
||||
# Add pep0484 and docstring knowledge.
|
||||
pep0484_hints = pep0484.follow_param(evaluator, param)
|
||||
doc_params = docstrings.follow_param(evaluator, param)
|
||||
if pep0484_hints or doc_params:
|
||||
return list(set(pep0484_hints) | set(doc_params))
|
||||
|
||||
if isinstance(param, ExecutedParam):
|
||||
return res_new | param.eval(evaluator)
|
||||
else:
|
||||
# Param owns no information itself.
|
||||
res_new |= dynamic.search_params(evaluator, param)
|
||||
if not res_new:
|
||||
if param.stars:
|
||||
t = 'tuple' if param.stars == 1 else 'dict'
|
||||
typ = list(evaluator.BUILTINS.py__getattribute__(t))[0]
|
||||
res_new = evaluator.execute(typ)
|
||||
if param.default:
|
||||
res_new |= evaluator.eval_element(context, param.default)
|
||||
return res_new
|
||||
|
||||
|
||||
def _check_flow_information(context, 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.::
|
||||
@@ -584,31 +353,6 @@ def _check_isinstance_type(context, element, search_name):
|
||||
return result
|
||||
|
||||
|
||||
def global_names_dict_generator(evaluator, scope, position):
|
||||
in_func = False
|
||||
while scope is not None:
|
||||
if not (scope.type == 'classdef' and in_func):
|
||||
# Names in methods cannot be resolved within the class.
|
||||
|
||||
for names_dict in scope.names_dicts(True):
|
||||
yield names_dict, position
|
||||
if hasattr(scope, 'resets_positions'):
|
||||
# TODO This is so ugly, seriously. However there's
|
||||
# currently no good way of influencing
|
||||
# global_names_dict_generator when it comes to certain
|
||||
# objects.
|
||||
position = None
|
||||
if scope.type == 'funcdef':
|
||||
# The position should be reset if the current scope is a function.
|
||||
in_func = True
|
||||
position = None
|
||||
scope = evaluator.wrap(scope.get_parent_scope())
|
||||
|
||||
# Add builtins to the global scope.
|
||||
for names_dict in evaluator.BUILTINS.names_dicts(True):
|
||||
yield names_dict, None
|
||||
|
||||
|
||||
def check_tuple_assignments(evaluator, types, name):
|
||||
"""
|
||||
Checks if tuples are assigned.
|
||||
@@ -627,19 +371,3 @@ def check_tuple_assignments(evaluator, types, name):
|
||||
return set()
|
||||
types = lazy_context.infer()
|
||||
return types
|
||||
|
||||
|
||||
def filter_private_variable(scope, origin_node):
|
||||
"""Check if a variable is defined inside the same class or outside."""
|
||||
instance = scope.get_parent_scope()
|
||||
coming_from = origin_node
|
||||
while coming_from is not None \
|
||||
and not isinstance(coming_from, (tree.Class, compiled.CompiledObject)):
|
||||
coming_from = coming_from.get_parent_scope()
|
||||
|
||||
# CompiledObjects don't have double underscore attributes, but Jedi abuses
|
||||
# those for fakes (builtins.pym -> list).
|
||||
if isinstance(instance, compiled.CompiledObject):
|
||||
return instance != coming_from
|
||||
else:
|
||||
return isinstance(instance, er.Instance) and instance.base.base != coming_from
|
||||
|
||||
@@ -42,19 +42,6 @@ def deep_ast_copy(obj, parent=None, new_elements=None):
|
||||
new_children.append(new_child)
|
||||
new_obj.children = new_children
|
||||
|
||||
# Copy the names_dict (if there is one).
|
||||
try:
|
||||
names_dict = obj.names_dict
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
new_obj.names_dict = new_names_dict = {}
|
||||
except AttributeError: # Impossible to set CompFor.names_dict
|
||||
pass
|
||||
else:
|
||||
for string, names in names_dict.items():
|
||||
new_names_dict[string] = [new_elements[n] for n in names]
|
||||
return new_obj
|
||||
|
||||
if isinstance(obj, tree.BaseNode):
|
||||
|
||||
Reference in New Issue
Block a user