forked from VimPlug/jedi
Complete writing the full mixed objects module.
This commit is contained in:
@@ -102,7 +102,7 @@ class LazyName(helpers.FakeName):
|
|||||||
'should be part of sys.modules.')
|
'should be part of sys.modules.')
|
||||||
|
|
||||||
if parser_path:
|
if parser_path:
|
||||||
assert len(parser_path) == 1
|
#assert len(parser_path) == 1
|
||||||
found = list(self._evaluator.find_types(mod, parser_path[0],
|
found = list(self._evaluator.find_types(mod, parser_path[0],
|
||||||
search_global=True))
|
search_global=True))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -234,10 +234,40 @@ class CompiledObject(Base):
|
|||||||
return [] # Builtins don't have imports
|
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):
|
class LazyNamesDict(object):
|
||||||
"""
|
"""
|
||||||
A names_dict instance for compiled objects, resembles the parser.tree.
|
A names_dict instance for compiled objects, resembles the parser.tree.
|
||||||
"""
|
"""
|
||||||
|
name_class = CompiledName
|
||||||
|
|
||||||
def __init__(self, evaluator, compiled_obj, is_instance):
|
def __init__(self, evaluator, compiled_obj, is_instance):
|
||||||
self._evaluator = evaluator
|
self._evaluator = evaluator
|
||||||
self._compiled_obj = compiled_obj
|
self._compiled_obj = compiled_obj
|
||||||
@@ -252,7 +282,7 @@ class LazyNamesDict(object):
|
|||||||
getattr(self._compiled_obj.obj, name)
|
getattr(self._compiled_obj.obj, name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise KeyError('%s in %s not found.' % (name, self._compiled_obj))
|
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):
|
def values(self):
|
||||||
obj = self._compiled_obj.obj
|
obj = self._compiled_obj.obj
|
||||||
@@ -265,40 +295,12 @@ class LazyNamesDict(object):
|
|||||||
# The dir function can be wrong.
|
# The dir function can be wrong.
|
||||||
pass
|
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:
|
if not inspect.ismodule(obj) and obj != type and not self._is_instance:
|
||||||
values += create(self._evaluator, type).names_dict.values()
|
values += create(self._evaluator, type).names_dict.values()
|
||||||
return 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):
|
def dotted_from_fs_path(fs_path, sys_path):
|
||||||
"""
|
"""
|
||||||
Changes `/usr/lib/python3.4/email/utils.py` to `email.utils`. I.e.
|
Changes `/usr/lib/python3.4/email/utils.py` to `email.utils`. I.e.
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ Used only for REPL Completion.
|
|||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
from jedi.parser import load_grammar
|
|
||||||
from jedi.parser.fast import FastParser
|
from jedi.parser.fast import FastParser
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
|
from jedi.cache import underscore_memoization, memoize_method
|
||||||
|
|
||||||
|
|
||||||
class MixedObject(object):
|
class MixedObject(object):
|
||||||
@@ -20,10 +20,41 @@ class MixedObject(object):
|
|||||||
This combined logic makes it possible to provide more powerful REPL
|
This combined logic makes it possible to provide more powerful REPL
|
||||||
completion. It allows side effects that are not noticable with the default
|
completion. It allows side effects that are not noticable with the default
|
||||||
parser structure to still be completeable.
|
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._evaluator = evaluator
|
||||||
self.obj = obj
|
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):
|
def _load_module(evaluator, path, python_object):
|
||||||
@@ -34,30 +65,54 @@ def _load_module(evaluator, path, python_object):
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
def find_syntax_node(evaluator, python_object):
|
def find_syntax_node_name(evaluator, python_object):
|
||||||
path = inspect.getsourcefile(python_object)
|
path = inspect.getsourcefile(python_object)
|
||||||
if path is None:
|
if path is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
module = _load_module(evaluator, path, python_object)
|
module = _load_module(evaluator, path, python_object)
|
||||||
|
|
||||||
if inspect.ismodule(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
|
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:
|
try:
|
||||||
code = python_object.__code__
|
code = python_object.__code__
|
||||||
except AttributeError:
|
|
||||||
# By using the line number of a code object we make the lookup in a
|
# 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
|
# 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
|
# stuff like ``a = 3; foo(a); a = 4`` on the same line, but if people
|
||||||
# do so we just don't care.
|
# do so we just don't care.
|
||||||
line_nr = code.co_firstlineno
|
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):
|
def create(evaluator, obj):
|
||||||
node = find_syntax_node(obj)
|
name = find_syntax_node_name(obj)
|
||||||
if node is None:
|
if name is None:
|
||||||
return compiled.create(evaluator, obj)
|
return compiled.create(evaluator, obj)
|
||||||
else:
|
else:
|
||||||
return MixedObject(evaluator, obj)
|
return MixedObject(evaluator, obj, name)
|
||||||
|
|||||||
Reference in New Issue
Block a user