1
0
forked from VimPlug/jedi

Filters for compiled objects and also FunctionExecution.

This commit is contained in:
Dave Halter
2016-10-02 15:36:24 +02:00
parent 249049b10c
commit c2873792eb
4 changed files with 97 additions and 5 deletions

View File

@@ -12,6 +12,7 @@ from jedi import debug
from jedi.cache import underscore_memoization, memoize_method
from jedi.parser.tree import Param, Base, Operator
from jedi.evaluate.helpers import FakeName
from jedi.evaluate.filters import AbstractFilter
from . import fake
@@ -156,6 +157,9 @@ class CompiledObject(Base):
def names_dicts(self, search_global, is_instance=False):
return self._names_dict_ensure_one_dict(is_instance)
def get_filters(self, search_global, is_instance=False):
yield self._ensure_one_filter(is_instance)
@memoize_method
def _names_dict_ensure_one_dict(self, is_instance):
"""
@@ -164,6 +168,14 @@ class CompiledObject(Base):
"""
return [LazyNamesDict(self._evaluator, self, is_instance)]
@memoize_method
def _ensure_one_filter(self, is_instance):
"""
search_global shouldn't change the fact that there's one dict, this way
there's only one `object`.
"""
return CompiledObjectFilter(self._evaluator, self, is_instance)
def get_subscope_by_name(self, name):
if name in dir(self.obj):
return CompiledName(self._evaluator, self, name).parent
@@ -314,6 +326,50 @@ class LazyNamesDict(object):
return values
class CompiledObjectFilter(AbstractFilter):
"""
A names_dict instance for compiled objects, resembles the parser.tree.
"""
name_class = CompiledName
def __init__(self, evaluator, compiled_obj, is_instance=False):
self._evaluator = evaluator
self._compiled_obj = compiled_obj
self._is_instance = is_instance
@memoize_method
def get(self, name, until_position=None):
name = str(name)
try:
getattr(self._compiled_obj.obj, name)
except AttributeError:
return []
except Exception:
# This is a bit ugly. We're basically returning this to make
# lookups possible without having the actual attribute. However
# this makes proper completion possible.
return [FakeName(name, create(self._evaluator, None), is_definition=True)]
return [self.name_class(self._evaluator, self._compiled_obj, name)]
def values(self, until_position=None):
raise NotImplementedError
obj = self._compiled_obj.obj
values = []
for name in dir(obj):
try:
values.append(self[name])
except KeyError:
# The dir function can be wrong.
pass
is_instance = self._is_instance or fake.is_class_instance(obj)
# ``dir`` doesn't include the type names.
if not inspect.ismodule(obj) and obj != type and not is_instance:
values += create(self._evaluator, type).names_dict.values()
return values
def dotted_from_fs_path(fs_path, sys_path):
"""
Changes `/usr/lib/python3.4/email/utils.py` to `email.utils`. I.e.

View File

@@ -32,12 +32,12 @@ class ParserTreeFilter(AbstractFilter):
def _filter(self, names, until_position):
names = super(ParserTreeFilter, self)._filter(names, until_position)
names = [n for n in names if n.is_definition()]
names = [n for n in names if n.get_parent_scope() == self._parser_scope]
names = [n for n in names if n.parent.get_parent_scope() == self._parser_scope]
return names
def get(self, name, until_position=None):
try:
names = self._used_names[name]
names = self._used_names[str(name)]
except KeyError:
return []
@@ -45,3 +45,14 @@ class ParserTreeFilter(AbstractFilter):
def values(self, name, until_position=None):
return self._filter(self._used_names.values(), until_position)
class FunctionExecutionFilter(ParserTreeFilter):
def __init__(self, parser_scope, executed_function):
super(FunctionExecutionFilter, self).__init__(parser_scope)
self._executed_function = executed_function
def _filter(self, names, until_position):
result = super(FunctionExecutionFilter, self)._filter(names, until_position)
return [self._executed_function.name_for_position(name.start_pos) for name in result]

View File

@@ -34,6 +34,7 @@ from jedi.evaluate import flow_analysis
from jedi.evaluate import param
from jedi.evaluate import helpers
from jedi.evaluate.cache import memoize_default
from jedi.evaluate.filters import ParserTreeFilter
def filter_after_position(names, position, origin=None):
@@ -238,6 +239,14 @@ class NameFinder(object):
`names_dicts`), until a name fits.
"""
names = []
for filter in get_global_filters(self._evaluator, self.scope):
names = filter.get(self.name_str, self.position)
if names:
break
debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str,
self.scope, names, self.position)
return names
for names_dict, position in names_dicts:
names = self.names_dict_lookup(names_dict, position)
if names:
@@ -365,6 +374,8 @@ def _name_to_types(evaluator, name, scope):
types = evaluator.eval_element(typ.node_from_name(name))
elif isinstance(typ, tree.Import):
types = imports.ImportWrapper(evaluator, name).follow()
elif typ.isinstance(tree.Function, tree.Class):
types = [evaluator.wrap(typ)]
elif typ.type == 'global_stmt':
for s in _get_global_stmt_scopes(evaluator, typ, name):
finder = NameFinder(evaluator, s, str(name))
@@ -610,6 +621,20 @@ def global_names_dict_generator(evaluator, scope, position):
yield names_dict, None
def get_global_filters(evaluator, context):
"""
Returns all filters in order of priority for name resolution.
"""
while context is not None:
for filter in context.get_filters(search_global=True):
yield filter
context = evaluator.wrap(context.get_parent_scope())
# Add builtins to the global scope.
for filter in evaluator.BUILTINS.get_filters(search_global=True):
yield filter
def check_tuple_assignments(evaluator, types, name):
"""
Checks if tuples are assigned.

View File

@@ -55,7 +55,7 @@ from jedi.evaluate import helpers
from jedi.evaluate import param
from jedi.evaluate import flow_analysis
from jedi.evaluate import imports
from jedi.evaluate.filters import ParserTreeFilter
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter
class Executed(tree.Base):
@@ -665,7 +665,7 @@ class FunctionExecution(Executed):
def __init__(self, evaluator, base, *args, **kwargs):
super(FunctionExecution, self).__init__(evaluator, base, *args, **kwargs)
self._copy_dict = {}
funcdef = base.base_func
self._original_function = funcdef = base.base_func
if isinstance(funcdef, mixed.MixedObject):
# The extra information in mixed is not needed anymore. We can just
# unpack it and give it the tree object.
@@ -777,7 +777,7 @@ class FunctionExecution(Executed):
del evaluator.predefined_if_name_dict_dict[for_stmt]
def get_filters(self, search_global):
yield ParserTreeFilter(self.base)
yield FunctionExecutionFilter(self._original_function, self.base.base_func)
@memoize_default(default=NO_DEFAULT)
def _get_params(self):