mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
Use even more stubs to get more complex completions for e.g. strings working
This commit is contained in:
@@ -118,7 +118,7 @@ def add_attribute_error(name_context, lookup_context, name):
|
||||
# Check for __getattr__/__getattribute__ existance and issue a warning
|
||||
# instead of an error, if that happens.
|
||||
typ = Error
|
||||
if lookup_context.is_instance():
|
||||
if lookup_context.is_instance() and not lookup_context.is_compiled():
|
||||
slot_names = lookup_context.get_function_slot_names(u'__getattr__') + \
|
||||
lookup_context.get_function_slot_names(u'__getattribute__')
|
||||
for n in slot_names:
|
||||
|
||||
@@ -19,6 +19,8 @@ from jedi.evaluate.utils import safe_property
|
||||
from jedi.evaluate.cache import evaluator_as_method_param_cache
|
||||
from jedi.cache import memoize_method
|
||||
|
||||
_sentinel = object()
|
||||
|
||||
|
||||
class HelperContextMixin(object):
|
||||
def get_root_context(self):
|
||||
@@ -185,6 +187,11 @@ class Context(HelperContextMixin, BaseContext):
|
||||
return clean_scope_docstring(self.tree_node)
|
||||
return None
|
||||
|
||||
def get_safe_value(self, default=_sentinel):
|
||||
if default is _sentinel:
|
||||
raise ValueError("There exists no safe value for context %s" % self)
|
||||
return default
|
||||
|
||||
def py__call__(self, arguments):
|
||||
debug.warning("no execution possible %s", self)
|
||||
return NO_CONTEXTS
|
||||
|
||||
@@ -282,6 +282,9 @@ class DirectObjectAccess(object):
|
||||
def is_module(self):
|
||||
return inspect.ismodule(self._obj)
|
||||
|
||||
def is_instance(self):
|
||||
return _is_class_instance(self._obj)
|
||||
|
||||
def ismethoddescriptor(self):
|
||||
return inspect.ismethoddescriptor(self._obj)
|
||||
|
||||
|
||||
@@ -110,6 +110,9 @@ class CompiledObject(Context):
|
||||
def is_stub(self):
|
||||
return False
|
||||
|
||||
def is_instance(self):
|
||||
return self.access_handle.is_instance()
|
||||
|
||||
def py__doc__(self):
|
||||
return self.access_handle.py__doc__()
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ from jedi.file_io import FileIO
|
||||
from jedi.evaluate.base_context import ContextSet, ContextWrapper
|
||||
from jedi.evaluate.context import ModuleContext
|
||||
from jedi.evaluate.cache import evaluator_function_cache
|
||||
from jedi.evaluate.helpers import execute_evaluated
|
||||
from jedi.evaluate.compiled.getattr_static import getattr_static
|
||||
from jedi.evaluate.compiled.access import compiled_objects_cache
|
||||
from jedi.evaluate.compiled.context import create_cached_compiled_object
|
||||
from jedi.evaluate.gradual.conversion import to_stub
|
||||
|
||||
_sentinel = object()
|
||||
|
||||
|
||||
class MixedObject(ContextWrapper):
|
||||
"""
|
||||
@@ -49,6 +50,12 @@ class MixedObject(ContextWrapper):
|
||||
def py__call__(self, arguments):
|
||||
return (to_stub(self._wrapped_context) or self._wrapped_context).py__call__(arguments)
|
||||
|
||||
def get_safe_value(self, default=_sentinel):
|
||||
if default is _sentinel:
|
||||
return self.compiled_object.get_safe_value()
|
||||
else:
|
||||
return self.compiled_object.get_safe_value(default)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s: %s>' % (
|
||||
type(self).__name__,
|
||||
@@ -210,9 +217,14 @@ def _create(evaluator, access_handle, parent_context, *args):
|
||||
)
|
||||
|
||||
result = _find_syntax_node_name(evaluator, access_handle)
|
||||
# TODO use stub contexts here. If we do that we probably have to care about
|
||||
# generics from stuff like `[1]`.
|
||||
if result is None:
|
||||
# TODO Care about generics from stuff like `[1]` and don't return like this.
|
||||
python_object = access_handle.access._obj
|
||||
if type(python_object) in (dict, list, tuple):
|
||||
return ContextSet({compiled_object})
|
||||
|
||||
tree_contexts = to_stub(compiled_object)
|
||||
if not tree_contexts:
|
||||
return ContextSet({compiled_object})
|
||||
else:
|
||||
module_node, tree_node, file_io, code_lines = result
|
||||
|
||||
@@ -157,7 +157,7 @@ class AbstractInstanceContext(Context):
|
||||
|
||||
def iterate():
|
||||
for generator in self.execute_function_slots(iter_slot_names):
|
||||
if generator.is_instance():
|
||||
if generator.is_instance() and not generator.is_compiled():
|
||||
# `__next__` logic.
|
||||
if self.evaluator.environment.version_info.major == 2:
|
||||
name = u'next'
|
||||
|
||||
@@ -189,13 +189,13 @@ class NameFinder(object):
|
||||
contexts = ContextSet.from_sets(name.infer() for name in names)
|
||||
|
||||
debug.dbg('finder._names_to_types: %s -> %s', names, contexts)
|
||||
if not names and self._context.is_instance():
|
||||
if not names and self._context.is_instance() and not self._context.is_compiled():
|
||||
# handling __getattr__ / __getattribute__
|
||||
return self._check_getattr(self._context)
|
||||
|
||||
# Add isinstance and other if/assert knowledge.
|
||||
if not contexts and isinstance(self._name, tree.Name) and \
|
||||
not self._name_context.is_instance():
|
||||
not self._name_context.is_instance() and not self._context.is_compiled():
|
||||
flow_scope = self._name
|
||||
base_nodes = [self._name_context.tree_node]
|
||||
|
||||
|
||||
@@ -207,7 +207,6 @@ def is_literal(context):
|
||||
|
||||
|
||||
def _get_safe_value_or_none(context, accept):
|
||||
if context.is_compiled():
|
||||
value = context.get_safe_value(default=None)
|
||||
if isinstance(value, accept):
|
||||
return value
|
||||
|
||||
@@ -212,11 +212,12 @@ def builtins_getattr(objects, names, defaults=None):
|
||||
# follow the first param
|
||||
for obj in objects:
|
||||
for name in names:
|
||||
if is_string(name):
|
||||
return obj.py__getattribute__(force_unicode(name.get_safe_value()))
|
||||
else:
|
||||
string = get_str_or_none(name)
|
||||
if string is None:
|
||||
debug.warning('getattr called without str')
|
||||
continue
|
||||
else:
|
||||
return obj.py__getattribute__(force_unicode(string))
|
||||
return NO_CONTEXTS
|
||||
|
||||
|
||||
|
||||
@@ -167,6 +167,14 @@ def test_list():
|
||||
['upper'])
|
||||
|
||||
|
||||
def test_getattr():
|
||||
class Foo1:
|
||||
bar = []
|
||||
baz = 'bar'
|
||||
_assert_interpreter_complete('getattr(Foo1, baz).app', locals(), ['append'])
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='For now slicing on strings is not supported for mixed objects')
|
||||
def test_slice():
|
||||
class Foo1:
|
||||
bar = []
|
||||
|
||||
Reference in New Issue
Block a user