1
0
forked from VimPlug/jedi

Complete writing the full mixed objects module.

This commit is contained in:
Dave Halter
2016-05-17 17:44:22 +02:00
parent 5595fb3e2f
commit ef314a5c38
3 changed files with 97 additions and 40 deletions

View File

@@ -102,7 +102,7 @@ class LazyName(helpers.FakeName):
'should be part of sys.modules.')
if parser_path:
assert len(parser_path) == 1
#assert len(parser_path) == 1
found = list(self._evaluator.find_types(mod, parser_path[0],
search_global=True))
else:

View File

@@ -234,10 +234,40 @@ class CompiledObject(Base):
return [] # Builtins don't have imports
class CompiledName(FakeName):
def __init__(self, evaluator, compiled_obj, name):
super(CompiledName, self).__init__(name)
self._evaluator = evaluator
self._compiled_obj = compiled_obj
self.name = name
def __repr__(self):
try:
name = self._compiled_obj.name # __name__ is not defined all the time
except AttributeError:
name = None
return '<%s: (%s).%s>' % (type(self).__name__, name, self.name)
def is_definition(self):
return True
@property
@underscore_memoization
def parent(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.
class LazyNamesDict(object):
"""
A names_dict instance for compiled objects, resembles the parser.tree.
"""
name_class = CompiledName
def __init__(self, evaluator, compiled_obj, is_instance):
self._evaluator = evaluator
self._compiled_obj = compiled_obj
@@ -252,7 +282,7 @@ class LazyNamesDict(object):
getattr(self._compiled_obj.obj, name)
except AttributeError:
raise KeyError('%s in %s not found.' % (name, self._compiled_obj))
return [CompiledName(self._evaluator, self._compiled_obj, name)]
return [self.name_class(self._evaluator, self._compiled_obj, name)]
def values(self):
obj = self._compiled_obj.obj
@@ -265,40 +295,12 @@ class LazyNamesDict(object):
# The dir function can be wrong.
pass
# dir doesn't include the type names.
# ``dir`` doesn't include the type names.
if not inspect.ismodule(obj) and obj != type and not self._is_instance:
values += create(self._evaluator, type).names_dict.values()
return values
class CompiledName(FakeName):
def __init__(self, evaluator, obj, name):
super(CompiledName, self).__init__(name)
self._evaluator = evaluator
self._obj = obj
self.name = name
def __repr__(self):
try:
name = self._obj.name # __name__ is not defined all the time
except AttributeError:
name = None
return '<%s: (%s).%s>' % (type(self).__name__, name, self.name)
def is_definition(self):
return True
@property
@underscore_memoization
def parent(self):
module = self._obj.get_parent_until()
return _create_from_name(self._evaluator, module, self._obj, self.name)
@parent.setter
def parent(self, value):
pass # Just ignore this, FakeName tries to overwrite the parent attribute.
def dotted_from_fs_path(fs_path, sys_path):
"""
Changes `/usr/lib/python3.4/email/utils.py` to `email.utils`. I.e.

View File

@@ -4,9 +4,9 @@ Used only for REPL Completion.
import inspect
from jedi.parser import load_grammar
from jedi.parser.fast import FastParser
from jedi.evaluate import compiled
from jedi.cache import underscore_memoization, memoize_method
class MixedObject(object):
@@ -20,10 +20,41 @@ class MixedObject(object):
This combined logic makes it possible to provide more powerful REPL
completion. It allows side effects that are not noticable with the default
parser structure to still be completeable.
The biggest difference from CompiledObject to MixedObject is that we are
generally dealing with Python code and not with C code. This will generate
fewer special cases, because we in Python you don't have the same freedoms
to modify the runtime.
"""
def __init__(self, evaluator, obj):
def __init__(self, evaluator, obj, node_name):
self._evaluator = evaluator
self.obj = obj
self.node_name = node_name
self._definition = node_name.get_definition()
def names_dicts(self):
return [LazyMixedNamesDict(self._evaluator, self, is_instance=False)]
def __getattr__(self, name):
return getattr(self._definition, name)
class MixedName(compiled.CompiledName):
"""
The ``CompiledName._compiled_object`` is our MixedObject.
"""
@property
@underscore_memoization
def parent(self):
return create(self._evaluator, getattr(self._compiled_obj, self.name))
@parent.setter
def parent(self, value):
pass # Just ignore this, FakeName tries to overwrite the parent attribute.
class LazyMixedNamesDict(compiled.LazyNamesDict):
name_class = MixedName
def _load_module(evaluator, path, python_object):
@@ -34,30 +65,54 @@ def _load_module(evaluator, path, python_object):
return module
def find_syntax_node(evaluator, python_object):
def find_syntax_node_name(evaluator, python_object):
path = inspect.getsourcefile(python_object)
if path is None:
return None
module = _load_module(evaluator, path, python_object)
if inspect.ismodule(python_object):
# We don't need to check names for modules, because there's not really
# a way to write a module in a module in Python (and also __name__ can
# be something like ``email.utils``).
return module
try:
names = module.used_names[python_object.__name__]
except NameError:
return None
names = [n for n in names if n.is_definition()]
try:
code = python_object.__code__
except AttributeError:
# By using the line number of a code object we make the lookup in a
# file pretty easy. There's still a possibility of people defining
# stuff like ``a = 3; foo(a); a = 4`` on the same line, but if people
# do so we just don't care.
line_nr = code.co_firstlineno
return None
except AttributeError:
pass
else:
line_names = [name for name in names if name.start_pos[0] == line_nr]
# There's a chance that the object is not available anymore, because
# the code has changed in the background.
if line_names:
return line_names[-1]
# It's really hard to actually get the right definition, here as a last
# resort we just return the last one. This chance might lead to odd
# completions at some points but will lead to mostly correct type
# inference, because people tend to define a public name in a module only
# once.
return names[-1]
@compiled_objects_cache
@compiled.compiled_objects_cache
def create(evaluator, obj):
node = find_syntax_node(obj)
if node is None:
name = find_syntax_node_name(obj)
if name is None:
return compiled.create(evaluator, obj)
else:
return MixedObject(evaluator, obj)
return MixedObject(evaluator, obj, name)