Deal better with instance conversions for stubs

This commit is contained in:
Dave Halter
2019-05-17 12:27:53 +02:00
parent f53c977069
commit b5d1e00930
3 changed files with 39 additions and 7 deletions

View File

@@ -9,6 +9,7 @@ from parso.python.parser import Parser
from parso.python import tree from parso.python import tree
from jedi._compatibility import u from jedi._compatibility import u
from jedi.evaluate.base_context import NO_CONTEXTS
from jedi.evaluate.syntax_tree import eval_atom from jedi.evaluate.syntax_tree import eval_atom
from jedi.evaluate.helpers import evaluate_call_of_leaf from jedi.evaluate.helpers import evaluate_call_of_leaf
from jedi.evaluate.compiled import get_string_context_set from jedi.evaluate.compiled import get_string_context_set
@@ -146,15 +147,22 @@ def evaluate_goto_definition(evaluator, context, leaf, prefer_stubs=False):
return evaluator.goto_definitions(context, leaf) return evaluator.goto_definitions(context, leaf)
parent = leaf.parent parent = leaf.parent
definitions = NO_CONTEXTS
if parent.type == 'atom': if parent.type == 'atom':
return context.eval_node(leaf.parent) definitions = context.eval_node(leaf.parent)
elif parent.type == 'trailer': elif parent.type == 'trailer':
return evaluate_call_of_leaf(context, leaf) definitions = evaluate_call_of_leaf(context, leaf)
elif isinstance(leaf, tree.Literal): elif isinstance(leaf, tree.Literal):
return eval_atom(context, leaf) return eval_atom(context, leaf)
elif leaf.type in ('fstring_string', 'fstring_start', 'fstring_end'): elif leaf.type in ('fstring_string', 'fstring_start', 'fstring_end'):
return get_string_context_set(evaluator) return get_string_context_set(evaluator)
return [] if prefer_stubs:
return definitions
from jedi.evaluate.gradual.conversion import try_stubs_to_actual_context_set
return try_stubs_to_actual_context_set(
definitions,
prefer_stub_to_compiled=True,
)
CallSignatureDetails = namedtuple( CallSignatureDetails = namedtuple(

View File

@@ -9,8 +9,19 @@ def stub_to_actual_context_set(stub_context, ignore_compiled=False):
if not stub_module.is_stub(): if not stub_module.is_stub():
return ContextSet([stub_context]) return ContextSet([stub_context])
was_instance = stub_context.is_instance()
if was_instance:
stub_context = stub_context.py__class__()
qualified_names = stub_context.get_qualified_names() qualified_names = stub_context.get_qualified_names()
return _infer_from_stub(stub_module, qualified_names, ignore_compiled) contexts = _infer_from_stub(stub_module, qualified_names, ignore_compiled)
if was_instance:
contexts = ContextSet.from_sets(
c.execute_evaluated()
for c in contexts
if c.is_class()
)
return contexts
def _infer_from_stub(stub_module, qualified_names, ignore_compiled): def _infer_from_stub(stub_module, qualified_names, ignore_compiled):
@@ -83,6 +94,10 @@ def _to_stub(context):
if context.is_stub(): if context.is_stub():
return ContextSet([context]) return ContextSet([context])
was_instance = context.is_instance()
if was_instance:
context = context.py__class__()
qualified_names = context.get_qualified_names() qualified_names = context.get_qualified_names()
stub_module = _load_stub_module(context.get_root_context()) stub_module = _load_stub_module(context.get_root_context())
if stub_module is None or qualified_names is None: if stub_module is None or qualified_names is None:
@@ -91,4 +106,11 @@ def _to_stub(context):
stub_contexts = ContextSet([stub_module]) stub_contexts = ContextSet([stub_module])
for name in qualified_names: for name in qualified_names:
stub_contexts = stub_contexts.py__getattribute__(name) stub_contexts = stub_contexts.py__getattribute__(name)
if was_instance:
stub_contexts = ContextSet.from_sets(
c.execute_evaluated()
for c in stub_contexts
if c.is_class()
)
return stub_contexts return stub_contexts

View File

@@ -60,10 +60,11 @@ def test_function(Script, environment):
def test_keywords_variable(Script): def test_keywords_variable(Script):
code = 'import keyword; keyword.kwlist' code = 'import keyword; keyword.kwlist'
def_, = Script(code).goto_definitions() seq1, seq2 = Script(code).goto_definitions()
assert def_.name == 'Sequence' assert seq1.name, seq2.name == 'Sequence'
# This points towards the typeshed implementation # This points towards the typeshed implementation
assert typeshed.TYPESHED_PATH in def_.module_path stub_seq, = seq1.goto_stubs()
assert typeshed.TYPESHED_PATH in stub_seq.module_path
def test_class(Script): def test_class(Script):
@@ -187,6 +188,7 @@ def _assert_is_same(d1, d2):
'code', [ 'code', [
'import os; os.walk', 'import os; os.walk',
'from collections import Counter; Counter', 'from collections import Counter; Counter',
'from collections import Counter; Counter()',
'from collections import Counter; Counter.most_common', 'from collections import Counter; Counter.most_common',
]) ])
def test_goto_stubs_on_itself(Script, code, type_): def test_goto_stubs_on_itself(Script, code, type_):