mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
Avoid evaluating properties just for the api type, improves #1933
This commit is contained in:
@@ -142,9 +142,9 @@ class MixedObjectFilter(compiled.CompiledValueFilter):
|
||||
super().__init__(inference_state, compiled_value)
|
||||
self._tree_value = tree_value
|
||||
|
||||
def _create_name(self, name):
|
||||
def _create_name(self, *args, **kwargs):
|
||||
return MixedName(
|
||||
super()._create_name(name),
|
||||
super()._create_name(*args, **kwargs),
|
||||
self._tree_value,
|
||||
)
|
||||
|
||||
|
||||
@@ -311,11 +311,12 @@ class CompiledModule(CompiledValue):
|
||||
|
||||
|
||||
class CompiledName(AbstractNameDefinition):
|
||||
def __init__(self, inference_state, parent_value, name):
|
||||
def __init__(self, inference_state, parent_value, name, is_descriptor):
|
||||
self._inference_state = inference_state
|
||||
self.parent_context = parent_value.as_context()
|
||||
self._parent_value = parent_value
|
||||
self.string_name = name
|
||||
self.is_descriptor = is_descriptor
|
||||
|
||||
def py__doc__(self):
|
||||
return self.infer_compiled_value().py__doc__()
|
||||
@@ -342,6 +343,11 @@ class CompiledName(AbstractNameDefinition):
|
||||
|
||||
@property
|
||||
def api_type(self):
|
||||
if self.is_descriptor:
|
||||
# In case of properties we want to avoid executions as much as
|
||||
# possible. Since the api_type can be wrong for other reasons
|
||||
# anyway, we just return instance here.
|
||||
return "instance"
|
||||
return self.infer_compiled_value().api_type
|
||||
|
||||
def infer(self):
|
||||
@@ -456,14 +462,14 @@ class CompiledValueFilter(AbstractFilter):
|
||||
|
||||
if self.is_instance and not in_dir_callback(name):
|
||||
return []
|
||||
return [self._get_cached_name(name)]
|
||||
return [self._get_cached_name(name, is_descriptor=is_descriptor)]
|
||||
|
||||
@memoize_method
|
||||
def _get_cached_name(self, name, is_empty=False):
|
||||
def _get_cached_name(self, name, is_empty=False, *, is_descriptor=False):
|
||||
if is_empty:
|
||||
return EmptyCompiledName(self._inference_state, name)
|
||||
else:
|
||||
return self._create_name(name)
|
||||
return self._create_name(name, is_descriptor=is_descriptor)
|
||||
|
||||
def values(self):
|
||||
from jedi.inference.compiled import builtin_from_name
|
||||
@@ -487,11 +493,12 @@ class CompiledValueFilter(AbstractFilter):
|
||||
names += filter.values()
|
||||
return names
|
||||
|
||||
def _create_name(self, name):
|
||||
def _create_name(self, name, is_descriptor):
|
||||
return CompiledName(
|
||||
self._inference_state,
|
||||
self.compiled_value,
|
||||
name
|
||||
name,
|
||||
is_descriptor,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
@@ -102,11 +102,13 @@ def test_side_effect_completion():
|
||||
assert foo.name == 'foo'
|
||||
|
||||
|
||||
def _assert_interpreter_complete(source, namespace, completions,
|
||||
**kwds):
|
||||
def _assert_interpreter_complete(source, namespace, completions, *, check_type=False, **kwds):
|
||||
script = jedi.Interpreter(source, [namespace], **kwds)
|
||||
cs = script.complete()
|
||||
actual = [c.name for c in cs]
|
||||
if check_type:
|
||||
for c in cs:
|
||||
c.type
|
||||
assert sorted(actual) == sorted(completions)
|
||||
|
||||
|
||||
@@ -757,3 +759,23 @@ def test_keyword_param_completion(code, expected):
|
||||
import random
|
||||
completions = jedi.Interpreter(code, [locals()]).complete()
|
||||
assert expected == [c.name for c in completions if c.name.endswith('=')]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('class_is_findable', [False, True])
|
||||
def test_avoid_descriptor_executions_if_not_necessary(class_is_findable):
|
||||
counter = 0
|
||||
class AvoidDescriptor(object):
|
||||
@property
|
||||
def prop(self):
|
||||
nonlocal counter
|
||||
counter += 1
|
||||
return self
|
||||
|
||||
if not class_is_findable:
|
||||
AvoidDescriptor.__name__ = "something_somewhere"
|
||||
namespace = {'b': AvoidDescriptor()}
|
||||
expected = ['prop']
|
||||
_assert_interpreter_complete('b.pro', namespace, expected, check_type=True)
|
||||
assert counter == 0
|
||||
_assert_interpreter_complete('b.prop.pro', namespace, expected, check_type=True)
|
||||
assert counter == 1
|
||||
|
||||
Reference in New Issue
Block a user